これはこれまでに尋ねられた最も馬鹿げた質問かもしれませんが、Javaの初心者にはかなり混乱していると思います。
- 誰かが不変の意味を明確にできますか?
- なぜ
String
不変ですか? - 不変オブジェクトの利点/欠点は何ですか?
StringBuilder
String などの可変オブジェクトを優先する必要があるのはなぜですか?
(Javaでの)良い例は本当にありがたいです。
これはこれまでに尋ねられた最も馬鹿げた質問かもしれませんが、Javaの初心者にはかなり混乱していると思います。
String
不変ですか?StringBuilder
String などの可変オブジェクトを優先する必要があるのはなぜですか?(Javaでの)良い例は本当にありがたいです。
回答:
不変とは、オブジェクトのコンストラクタが実行を完了すると、そのインスタンスを変更できないことを意味します。
これは、他の誰かがその内容を変更することを心配することなく、オブジェクトへの参照を渡すことができることを意味するので便利です。特に並行性を処理する場合、決して変更されないオブジェクトのロックの問題はありません
例えば
class Foo
{
private final String myvar;
public Foo(final String initialValue)
{
this.myvar = initialValue;
}
public String getValue()
{
return this.myvar;
}
}
Foo
呼び出し元getValue()
が文字列のテキストを変更する可能性があることを心配する必要はありません。
に似たクラスを想像しますが、メンバーとしてFoo
a StringBuilder
ではなくString
、の呼び出し元がインスタンスの属性getValue()
を変更できることがわかります。StringBuilder
Foo
また、さまざまな種類の不変性に注意してください。EricLippertがこれについてブログ記事を書いています。基本的には、インターフェイスは不変ですが、実際には変更可能なプライベート状態のオブジェクトを持つことができます(したがって、スレッド間で安全に共有することはできません)。
不変オブジェクトは、内部フィールド(または少なくとも、その外部動作に影響を与えるすべての内部フィールド)を変更できないオブジェクトです。
不変文字列には多くの利点があります。
パフォーマンス:次の操作を実行します。
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オブジェクトが変更可能である場合(文字は後で変更される可能性があります)、すべての文字をコピーする必要があります。そうしないと、部分文字列の文字への変更が後で他の文字列に反映されます。
並行性:不変オブジェクトの内部構造が有効である場合、それは常に有効です。異なるスレッドがそのオブジェクト内で無効な状態を作成する可能性はありません。したがって、不変オブジェクトはスレッドセーフです。
ガベージコレクション:ガベージコレクターは、不変オブジェクトについて論理的な決定を行う方がはるかに簡単です。
ただし、不変性には欠点もあります。
パフォーマンス:待って、パフォーマンスは不変性の利点だと言ったと思いました!まあ、それは時々ありますが、常にではありません。次のコードを見てください。
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つだけです)。また、他のどこからも参照されないため、スタックにのみ割り当てられるため、関数呼び出しが完了するとすぐに割り当てが解除されます(ガベージコレクションを心配する必要はありません)。また、可変性と不変性の両方のパフォーマンス上のメリットをすべて享受できます。
Passing pointers because Java is pass-by-reference
Javaは「値渡し」ではありませんか?
上記のウィキペディアの定義を使用する場合、実際にはStringは不変ではありません。
文字列の状態は、ポスト構造を変更します。hashcode()メソッドを見てください。文字列はローカルフィールドのハッシュコード値をキャッシュしますが、hashcode()の最初の呼び出しまでそれを計算しません。このハッシュコードの遅延評価により、Stringは、状態が変化する不変オブジェクトとして興味深い位置に配置されますが、リフレクションを使用しないと変化したことが確認できません。
したがって、不変の定義は、変更されたことが確認できないオブジェクトである必要があります。
作成後に不変オブジェクトの状態が変化しても誰もそれを(リフレクションなしで)見ることができない場合、オブジェクトはまだ不変ですか?
不変オブジェクトとは、プログラムで変更できないオブジェクトです。これらは、マルチスレッド環境や、複数のプロセスがオブジェクトの値を変更(変更)できる他の環境に特に適しています。
ただし、明確にするために、StringBuilderは実際には可変オブジェクトであり、不変オブジェクトではありません。通常のjava Stringは不変です(つまり、作成された後は、オブジェクトを変更せずに基になる文字列を変更することはできません)。
たとえば、文字列値と文字列色を持つ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!");
次に、文字列は常に「青」であると想定します。ただし、別のスレッドがこのインスタンスを取得して呼び出された場合
blueString.setColor("Red");
突然、そしておそらく予想外に、「青」のストリングが必要なときに「赤」のストリングができます。このため、オブジェクトのインスタンスを渡すときに、ほとんどの場合、不変オブジェクトが優先されます。変更可能なオブジェクトが本当に必要な場合は、通常、特定の制御フィールドからコピーを渡すだけでオブジェクトを保護します。
要約すると、Javaでは、java.lang.Stringは不変オブジェクト(作成後は変更できません)であり、java.lang.StringBuilderは新しいオブジェクトを作成せずに変更できるため、変更可能なオブジェクトです。
文字列s1 = "古い文字列";
//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 = "新しい文字列";
//s1 deletes reference to old string and points to the newly created one
[s1] -----|--------->| "New String" |
| | |
|~~~~~~~~~X| "Old String" |
[s2] ------------------------^
元の文字列「メモリ内」は変更されませんでしたが、参照変数が変更され、新しい文字列を参照するようになりました。また、s2がない場合でも、「古い文字列」はメモリに残りますが、アクセスできなくなります...
「不変」とは、値を変更できないことを意味します。Stringクラスのインスタンスがある場合、値を変更すると思われるメソッドを呼び出すと、実際には別のStringが作成されます。
String foo = "Hello";
foo.substring(3);
<-- foo here still has the same value "Hello"
変更を保存するには、次のようにする必要がありますfoo = foo.sustring(3);
コレクションを操作する場合、不変vs可変はおかしい場合があります。(:考えるヒントが値を変更後、あなたはマップのキーとして変更可能なオブジェクトを使用する場合に発生となるかについて考えequals
とhashCode
)。
少し遅いかもしれませんが、不変オブジェクトとは何かを理解するために、新しい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()…2014-03-18
dateAfterTwoYears.toString()…2016-03-18
SCJP Sun Certified Programmer for Java 5 Study Guideの説明が本当に気に入っています。
Javaのメモリ効率を高めるために、JVMは「文字列定数プール」と呼ばれる特別なメモリ領域を確保しています。コンパイラは文字列リテラルを検出すると、プールをチェックして、同じ文字列がすでに存在するかどうかを確認します。一致が見つかった場合、新しいリテラルへの参照は既存の文字列に送られ、新しい文字列リテラルオブジェクトは作成されません。
不変のオブジェクトは、作成後に状態を変更できません。
可能な限り不変オブジェクトを使用する主な理由は3つあります。これらすべてが、コードに導入するバグの数を減らすのに役立ちます。
オブジェクトの状態が不変であることがわかっている場合、たとえば、計算されたハッシュをキャッシュするなど、コードで行うことができる他の最適化もいくつかありますが、これらは最適化であるため、それほど興味深いものではありません。
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="Hi"
:オブジェクトs1
は "Hi"値で作成されました。
s2=s1
:s2
s1オブジェクトを参照してオブジェクトが作成されます。
s1="Bye"
:以前のs1
オブジェクトの値は変更されません。これs1
は、String型があり、String型が不変の型であるため、コンパイラは代わりに「Bye」値を使用して新しいStringオブジェクトを作成し、s1
それを参照します。ここでs2
値を出力すると、「Hi」の値を持つs2
以前のs1
オブジェクトを参照するため、結果は「Bye」ではなく「Hi」になります。
不変とは、単に変更できない、または変更できないことを意味します。文字列オブジェクトが作成されると、そのデータまたは状態は変更できません
以下の例を考えてみてください。
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」として作成された新しいオブジェクトを確認できます。しかし、「未来」を変えないでください。Because String is immutable
。s
、まだ「未来」を参照してください。「未来の世界」と呼ぶ必要があるなら
String s="Future";
s=s.concat(" World");
System.out.println(s);//print Future World
文字列オブジェクトがJavaで不変なのはなぜですか?
Javaは文字列リテラルの概念を使用しているためです。5つの参照変数があり、すべて1つのオブジェクト「Future」を参照しているとします。1つの参照変数がオブジェクトの値を変更すると、すべての参照変数に影響します。そのため、Javaでは文字列オブジェクトが不変です。
不変オブジェクト
作成後に状態を変更できない場合、オブジェクトは不変と見なされます。不変オブジェクトへの最大限の依存は、シンプルで信頼できるコードを作成するための適切な戦略として広く受け入れられています。
不変オブジェクトは、並行アプリケーションで特に役立ちます。それらは状態を変更できないため、スレッドの干渉によって破損したり、不整合な状態で観察されたりすることはありません。
プログラマは、オブジェクトを適切に更新するのではなく、新しいオブジェクトを作成するコストを心配するため、不変オブジェクトを採用することに消極的です。オブジェクト作成の影響は過大評価されることが多く、不変オブジェクトに関連するいくつかの効率によって相殺される可能性があります。これらには、ガベージコレクションによるオーバーヘッドの減少、および変更可能なオブジェクトを破損から保護するために必要なコードの排除が含まれます。
次のサブセクションでは、インスタンスが変更可能なクラスを受け取り、不変のインスタンスを持つクラスを派生させます。そうすることで、彼らはこの種の変換に関する一般的なルールを与え、不変オブジェクトのいくつかの利点を示します。
受け入れられた答えはすべての質問に答えるわけではないので。11年6か月後に答えを迫られます。
誰かが不変の意味を明確にできますか?
あなたが不変のオブジェクトを意味していることを願っています(不変の参照について考えることができたため)
オブジェクトは不変です。一度作成されると、常に同じ値を表します(値を変更するメソッドはありません)。
なぜ
String
不変ですか?
Sting.javaソースコードを調べて確認できる上記の定義を尊重してください。
不変オブジェクトの利点/欠点は何ですか?不変タイプは次のとおりです。
バグからより安全。
理解しやすいです。
変更の準備ができています。
StringBuilderなどの変更可能なオブジェクトをStringよりも優先する必要があるのはなぜですか?
質問を絞り込むなぜプログラミングに可変のStringBuilderが必要なのですか? これの一般的な用途は、次のように多数の文字列を連結することです。
String s = "";
for (int i = 0; i < n; ++i) {
s = s + n;
}
不変文字列を使用して、これは多くの一時的なコピーを作成します。文字列の最初の数( "0")は、最終的な文字列を構築する過程で実際にn回コピーされ、2番目の数はn-1回コピーされます。オン。実際には、n個の要素のみを連結したとしても、すべてのコピーを実行するには、O(n2)時間かかります。
StringBuilderは、このコピーを最小限に抑えるように設計されています。toString()呼び出しで最後の文字列を要求するときに、最後まで文字列のコピーをまったく行わないようにするために、シンプルですが巧妙な内部データ構造を使用します。
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; ++i) {
sb.append(String.valueOf(n));
}
String s = sb.toString();
可変オブジェクトを使用する理由の1つは、優れたパフォーマンスを得ることです。もう1つは共有に便利です。プログラムの2つの部分は、共通の可変データ構造を共有することで、より便利に通信できます。
さらにここで見つけることができます:https : //web.mit.edu/6.005/www/fa15/classes/09-immutability/#useful_immutable_types
不変オブジェクトは、作成後に変更できないオブジェクトです。典型的な例は文字列リテラルです。
ますます人気が高まるADプログラミング言語には、「不変」キーワードによる「不変性」の概念があります。-それについてこのDr.Dobbの記事をチェックhttp://dobbscodetalk.com/index.php?option=com_myblog&show=Invariant-Strings.html&Itemid=29を。問題を完全に説明しています。