質問
これはこれまでに聞かれた最も馬鹿げた質問かもしれませんが、Java初心者にとっては非常に混乱していると思います。
- 誰かが immutable の意味を明確にできますか?
-
String
が不変な理由 - 不変オブジェクトの長所と短所は何ですか?
- なぜ
StringBuilder
などの可変オブジェクトをStringよりも優先させるべきなのですか?
(Javaの)良い例は本当にありがたいです。
解決
Immutableは、オブジェクトのコンストラクターの実行が完了すると、そのインスタンスを変更できないことを意味します。
これは、他の誰かがその内容を変更することを心配することなく、オブジェクトへの参照を渡すことができるので便利です。 特に並行性を処理する場合、変更されないオブジェクトにはロックの問題はありません
e.g。
class Foo
{
private final String myvar;
public Foo(final String initialValue)
{
this.myvar = initialValue;
}
public String getValue()
{
return this.myvar;
}
}
Foo
は、 getValue()
の呼び出し側が文字列のテキストを変更することを心配する必要はありません。
Foo
に似たクラスを想像しますが、 String
ではなく StringBuilder
をメンバーとして使用すると、 getValue()
の呼び出し元は、 Foo
インスタンスの StringBuilder
属性を変更できます。
また、さまざまな種類の不変性に注意してください:Eric Lippertはこれに関するブログ記事。基本的に、インターフェイスが不変であるが、実際には可変のプライベート状態の背後にあるオブジェクトを持つことができます(したがって、スレッド間で安全に共有できません)。
他のヒント
不変オブジェクトとは、内部フィールド(または少なくとも、外部の動作に影響するすべての内部フィールド)を変更できないオブジェクトです。
不変文字列には多くの利点があります:
パフォーマンス:次の操作を実行します:
String substring = fullstring.substring(x,y);
substring()メソッドの基礎となるCは、おそらく次のようなものです。
// Assume string is stored like this:
struct String { char* characters; unsigned int length; };
// Passing pointers because Java is pass-by-reference
struct String* substring(struct String* in, unsigned int begin, unsigned int end)
{
struct String* out = malloc(sizeof(struct String));
out->characters = in->characters + begin;
out->length = end - begin;
return out;
}
どの文字もコピーする必要がないことに注意してください! Stringオブジェクトが変更可能であれば(文字は後で変更される可能性があります)、すべての文字をコピーする必要があります。部分文字列は後で他の文字列に反映されます。
並行性:不変オブジェクトの内部構造が有効な場合、それは常に有効です。異なるスレッドがそのオブジェクト内に無効な状態を作成する可能性はありません。したがって、不変オブジェクトは Thread Safe です。
ガベージコレクション:ガベージコレクタは、不変オブジェクトについて論理的な決定を下すのがはるかに簡単です。
ただし、不変性には欠点もあります:
パフォーマンス:待って、パフォーマンスは不変性の利点だと言ったと思います!まあ、それは時々ですが、常にではありません。次のコードを取得します。
foo = foo.substring(0,4) + "a" + foo.substring(5); // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder
2行とも、4番目の文字を文字「a」に置き換えます。コードの2番目の部分が読みやすいだけでなく、高速です。 fooの基になるコードをどのように行う必要があるかを見てください。部分文字列は簡単ですが、スペース5にすでに文字があり、他の何かがfooを参照している可能性があるため、単に変更することはできません。文字列全体をコピーする必要があります(もちろん、この機能の一部は実際の基盤となるCの関数に抽象化されますが、ここでのポイントは、すべて1か所で実行されるコードを表示することです)。
struct String* concatenate(struct String* first, struct String* second)
{
struct String* new = malloc(sizeof(struct String));
new->length = first->length + second->length;
new->characters = malloc(new->length);
int i;
for(i = 0; i < first->length; i++)
new->characters[i] = first->characters[i];
for(; i - first->length < second->length; i++)
new->characters[i] = second->characters[i - first->length];
return new;
}
// The code that executes
struct String* astring;
char a = 'a';
astring->characters = &a;
astring->length = 1;
foo = concatenate(concatenate(slice(foo,0,4),astring),slice(foo,5,foo->length));
連結は 2回と呼ばれることに注意してください。つまり、文字列全体をループスルーする必要があります。これを bar
操作のCコードと比較してください:
bar->characters[4] = 'a';
変更可能な文字列操作は明らかにはるかに高速です。
まとめ:ほとんどの場合、不変の文字列が必要です。しかし、文字列に多くの追加と挿入を行う必要がある場合は、速度のために可変性が必要です。並行性の安全性とガベージコレクションの利点を必要とする場合、キーは、可変オブジェクトをメソッドに対してローカルに保つことです:
// This will have awful performance if you don't use mutable strings
String join(String[] strings, String separator)
{
StringBuilder mutable;
boolean first = true;
for(int i = 0; i < strings.length; i++)
{
if(!first) first = false;
else mutable.append(separator);
mutable.append(strings[i]);
}
return mutable.toString();
}
mutable
オブジェクトはローカル参照であるため、同時実行の安全性について心配する必要はありません(1つのスレッドのみがこれに触れる)。また、他の場所では参照されないため、スタック上でのみ割り当てられるため、関数呼び出しが終了するとすぐに割り当てが解除されます(ガベージコレクションについて心配する必要はありません)。また、可変性と不変性の両方のパフォーマンス上の利点をすべて得ることができます。
上記のウィキペディア定義を使用する場合、実際には文字列は不変ではありません。
文字列の状態は、構築後を変更します。 hashcode()メソッドを見てください。文字列はハッシュコード値をローカルフィールドにキャッシュしますが、hashcode()の最初の呼び出しまで計算しません。ハッシュコードのこの遅延評価は、状態を変更する不変オブジェクトとしてStringを興味深い位置に配置しますが、リフレクションを使用せずに変更したことは観察できません。
したがって、不変の定義は、変更されたことが観察できないオブジェクトである必要があります。
不変オブジェクトの作成後に状態が変化しても、誰もそれを見ることができない場合(リフレクションなし)、オブジェクトは不変のままですか?
不変オブジェクトとは、プログラムで変更できないオブジェクトです。マルチスレッド環境や、複数のプロセスがオブジェクトの値を変更(変更)できる他の環境に特に適しています。
ただし、StringBuilderは実際には変更可能なオブジェクトであり、不変のオブジェクトではありません。通常のjava文字列は不変です(一度作成すると、オブジェクトを変更せずに基になる文字列を変更することはできません)。
たとえば、文字列の値と文字列の色を持つColoredStringというクラスがあるとします:
public class ColoredString {
private String color;
private String string;
public ColoredString(String color, String string) {
this.color = color;
this.string = string;
}
public String getColor() { return this.color; }
public String getString() { return this.string; }
public void setColor(String newColor) {
this.color = newColor;
}
}
この例では、新しいColoredStringクラスを作成せずにキープロパティの1つを変更(変更)できるため、ColoredStringは可変であると言われます。これが悪い理由は、たとえば、複数のスレッドを持つGUIアプリケーションがあり、ColoredStringsを使用してウィンドウにデータを印刷しているとします。
として作成されたColoredStringのインスタンスがある場合new ColoredString("Blue", "This is a blue string!");
その場合、文字列は常に&quot; Blue&quot;であると予想されます。ただし、別のスレッドがこのインスタンスを取得して呼び出された場合
blueString.setColor("Red");
突然、そしておそらく予期せず、「赤」になります。 &quot; Blue&quot;が必要なときの文字列1。このため、オブジェクトのインスタンスを渡す場合、ほとんどの場合、不変オブジェクトが優先されます。可変オブジェクトが本当に必要な場合は、通常、特定の制御フィールドからコピーを渡すだけでオブジェクトを保護します。
要約すると、Javaでは、java.lang.Stringは不変オブジェクトであり(作成後に変更することはできません)、java.lang.StringBuilderは変更せずに変更できるため、可変オブジェクトです。新しいインスタンスを作成します。
- 大規模なアプリケーションでは、文字列リテラルがメモリの大きなビットを占有するのが一般的です。したがって、メモリを効率的に処理するために、JVMは「文字列定数プール」と呼ばれる領域を割り当てます。(メモリでは、参照されていない文字列でさえchar []、その長さのint、およびそのhashCodeの別の文字列を持ちます。必要)
- コンパイラが文字列リテラルに遭遇すると、プールをチェックして、同じリテラルがすでに存在するかどうかを確認します。そして、見つかった場合、新しいリテラルへの参照は既存の文字列に向けられ、新しい「文字列リテラルオブジェクト」は作成されません(既存の文字列は単に追加の参照を取得します)。
- したがって、文字列の可変性はメモリを節約します...
- しかし、変数のいずれかが値を変更する場合、実際には-以下に示すように、メモリ内の値ではなく、参照のみが変更されます(したがって、それを参照する他の変数に影響しません)。
String s1 =&quot; Old string&quot ;;
//s1 variable, refers to string in memory
reference | MEMORY |
variables | |
[s1] --------------->| "Old String" |
文字列s2 = s1;
//s2 refers to same string as s1
| |
[s1] --------------->| "Old String" |
[s2] ------------------------^
s1 =&quot;新しい文字列&quot ;;
//s1 deletes reference to old string and points to the newly created one
[s1] -----|--------->| "New String" |
| | |
|~~~~~~~~~X| "Old String" |
[s2] ------------------------^
元の文字列「メモリ内」は変更されませんでしたが、 参照変数が変更され、新しい文字列を参照するようになりました。 s2がない場合は、&quot; Old String&quot;まだメモリ内にありますが、 アクセスできなくなります...
&quot;不変&quot;値を変更できないことを意味します。 Stringクラスのインスタンスがある場合、値を変更すると思われるメソッドを呼び出すと、実際に別のStringが作成されます。
String foo = "Hello";
foo.substring(3);
<-- foo here still has the same value "Hello"
変更を保存するには、次のようにする必要があります foo = foo.sustring(3);
Immutable vs Mutableは、コレクションを操作するときに面白い場合があります。可変オブジェクトをマップのキーとして使用し、値を変更するとどうなるかを考えます(ヒント: equals
および hashCode
について考えてください)。
java.time
もう少し遅れるかもしれませんが、不変オブジェクトが何であるかを理解するために、新しいJava 8 Date and Time API( java.time )。おそらくご存知のように、Java 8のすべての日付オブジェクトは不変であるため、次の例では
LocalDate date = LocalDate.of(2014, 3, 18);
date.plusYears(2);
System.out.println(date);
出力:
2014-03-18
これは、 plusYears(2)
が新しいオブジェクトを返すため、初期日付と同じ年を出力します。したがって、古い日付は不変オブジェクトであるため、古い日付は変更されません。一度作成すると、それをさらに変更することはできず、日付変数は引き続きそれを指します。
そのため、このコード例では、 plusYears
への呼び出しによってインスタンス化されて返される新しいオブジェクトをキャプチャして使用する必要があります。
LocalDate date = LocalDate.of(2014, 3, 18);
LocalDate dateAfterTwoYears = date.plusYears(2);
date.toString()&#8230; 2014-03-18
dateAfterTwoYears.toString()&#8230; 2016-03-18
SCJP Sun Certified Programmer for Java 5 Study Guide 。
Javaのメモリ効率を高めるために、JVMは「文字列定数プール」と呼ばれるメモリの特別な領域を確保します。コンパイラは、文字列リテラルを検出すると、プールをチェックして、同一の文字列が既に存在するかどうかを確認します。一致が見つかった場合、新しいリテラルへの参照は既存の文字列に向けられ、新しい文字列リテラルオブジェクトは作成されません。
不変のオブジェクトは、作成後に状態を変更できません。
可能な限り不変オブジェクトを使用する主な理由は3つあります。これらはすべて、コードに導入するバグの数を減らすのに役立ちます。
- オブジェクトの状態を別のメソッドで変更できないことがわかっている場合、プログラムの動作について推論するのは簡単です
- 不変オブジェクトは自動的にスレッドセーフであるため(安全に公開されていると想定)、ピン留めが難しいマルチスレッドバグの原因になることはありません
- 不変オブジェクトは常に同じハッシュコードを持つため、HashMap(または同様の)のキーとして使用できます。ハッシュテーブル内の要素のハッシュコードが変更された場合、テーブルエントリを見つけようとすると間違った場所を探すことになるため、テーブルエントリは事実上失われます。これがStringオブジェクトが不変である主な理由です。これらはHashMapキーとして頻繁に使用されます。
オブジェクトの状態が不変であることがわかっている場合(たとえば、計算されたハッシュをキャッシュするなど)、コード内で行うことができる他の最適化もありますが、これらは最適化であり、したがってあまり興味深いものではありません。
1つの意味は、値がコンピューターに保存される方法に関係します。たとえば、.Net文字列の場合、メモリ内の文字列を変更できないことを意味します。メモリ内に新しい文字列を作成し、既存の変数(実際の文字コレクションへのポインタ)を新しい文字列にポイントします。
String s1="Hi";
String s2=s1;
s1="Bye";
System.out.println(s2); //Hi (if String was mutable output would be: Bye)
System.out.println(s1); //Bye
s1 =&quot; Hi&quot;
:オブジェクト s1
が&quot; Hi&quot;で作成されましたその中の値。
s2 = s1
:オブジェクト s2
は、s1オブジェクトを参照して作成されます。
s1 =&quot; Bye&quot;
:以前の s1
オブジェクトの値は、 s1
がString型であり、String typeは不変型、代わりにコンパイラは&quot; Bye&quot;で新しいStringオブジェクトを作成します値とそれを参照する s1
ここで s2
の値を出力すると、結果は&quot; Hi&quot;になります。 「バイ」ではない s2
は、&quot; Hi&quot;を持っている以前の s1
オブジェクトを参照したためです。値。
Immutableは、オブジェクトが作成されると、そのメンバーのいずれも変更されないことを意味します。 String
は、コンテンツを変更できないため不変です。
例:
String s1 = " abc ";
String s2 = s1.trim();
上記のコードでは、文字列s1は変更されず、別のオブジェクト( s2
)が s1
を使用して作成されました。
変更不能とは、単に変更不可能または変更不可能を意味します。文字列オブジェクトが作成されると、そのデータまたは状態は変更できません
以下の例を検討してください
class Testimmutablestring{
public static void main(String args[]){
String s="Future";
s.concat(" World");//concat() method appends the string at the end
System.out.println(s);//will print Future because strings are immutable objects
}
}
次の図を考えてみましょう
この図では、「Future World」として作成された新しいオブジェクトを見ることができます。ただし、&quot; Future&quot;は変更しないでください。文字列は不変であるため
。 s
、まだ&quot; Future&quot;を参照してください。 「Future World」を呼び出す必要がある場合は、
String s="Future";
s=s.concat(" World");
System.out.println(s);//print Future World
javaでは文字列オブジェクトが不変なのはなぜですか?
Javaは文字列リテラルの概念を使用するため。 5つの参照変数があり、すべてが1つのオブジェクト「未来」を参照するとします。1つの参照変数がオブジェクトの値を変更すると、すべての参照変数に影響します。文字列オブジェクトがjavaで不変である理由です。
一度インスタンス化されると、変更できません。のインスタンスがハッシュテーブルなどのキーとして使用される可能性のあるクラスを考えます。 Javaのベストプラクティスをご覧ください。
不変オブジェクト
オブジェクトは、構築後に状態を変更できない場合、不変と見なされます。不変オブジェクトへの最大限の依存は、シンプルで信頼できるコードを作成するための健全な戦略として広く受け入れられています。
不変オブジェクトは、並行アプリケーションで特に役立ちます。状態を変更できないため、スレッドの干渉によって破損したり、一貫性のない状態で観察されたりすることはありません。
プログラマーは、不変オブジェクトの使用に消極的です。これは、オブジェクトを適切に更新するのではなく、新しいオブジェクトを作成するコストを心配するためです。オブジェクト作成の影響は多くの場合過大評価されており、不変オブジェクトに関連するいくつかの効率によって相殺できます。これには、ガベージコレクションによるオーバーヘッドの減少、および可変オブジェクトを破損から保護するために必要なコードの削除が含まれます。
次のサブセクションでは、インスタンスが可変であるクラスを取得し、そのインスタンスから不変のインスタンスを持つクラスを派生します。そうすることで、彼らはこの種の変換のための一般的なルールを与え、不変オブジェクトの利点のいくつかを示します。
のこのフレーズが好きですオブジェクトは、その後状態を変更できない場合、不変と見なされます 構築されます。不変オブジェクトへの最大の依存度は広く シンプルで信頼性の高いコードを作成するための健全な戦略として受け入れられています。
不変オブジェクトは、並行アプリケーションで特に役立ちます。 状態を変更できないため、スレッドによって破損することはありません 干渉または一貫性のない状態で観察されました。
不変オブジェクトは並行プログラミングを容易にします
不変オブジェクトとは、作成後に変更できないオブジェクトです。典型的な例は文字列リテラルです。
Dプログラミング言語は、ますます一般的になっていますが、「不変性」という概念があります。 「不変式」を通じてキーワード。 Dr.Dobbの記事について確認してください- http:// dobbscodetalk .com / index.php?option = com_myblog&amp; show = Invariant-Strings.html&amp; Itemid = 29 。問題を完全に説明します。