416

これは、これまでに尋ねられた中で最もばかげた質問かもしれませんが、Java の初心者にとってはかなり混乱していると思います。

  1. immutableの意味を誰かが明確にできますか?
  2. なぜString不変なのですか?
  3. 不変オブジェクトの利点/欠点は何ですか?
  4. StringBuilderString やその逆よりも、変更可能なオブジェクトを優先する必要があるのはなぜですか?

(Java での) 良い例を示していただければ幸いです。

4

17 に答える 17

276

不変とは、オブジェクトのコンストラクターが実行を完了すると、そのインスタンスを変更できないことを意味します。

これは、他の誰かが内容を変更することを心配することなく、オブジェクトへの参照を渡すことができることを意味するので便利です。特に同時実行性を扱う場合、決して変更されないオブジェクトにはロックの問題はありません

例えば

class Foo
{
     private final String myvar;

     public Foo(final String initialValue)
     {
         this.myvar = initialValue;
     }

     public String getValue()
     {
         return this.myvar;
     }
}

FoogetValue()の呼び出し元が文字列内のテキストを変更する可能性があることを心配する必要はありません。

と同様のクラスを想像すると、メンバーとして a ではなく a を使用すると、 の呼び出し元がFooインスタンスStringBuilderの属性を変更できることがわかります。StringgetValue()StringBuilderFoo

また、さまざまな種類の不変性にも注意してください。これについては、Eric Lippert がブログ記事を書いています。基本的に、インターフェイスが不変のオブジェクトを使用できますが、舞台裏では実際の可変のプライベート状態です (したがって、スレッド間で安全に共有することはできません)。

于 2008-11-10T23:11:48.477 に答える
82

不変オブジェクトとは、内部フィールド (または少なくとも、外部の動作に影響を与えるすべての内部フィールド) を変更できないオブジェクトです。

不変文字列には多くの利点があります。

パフォーマンス:次の操作を行います。

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));

concatenate が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 つのスレッドだけがそれに触れます)。また、他のどこからも参照されないため、スタック上にのみ割り当てられるため、関数呼び出しが終了するとすぐに割り当てが解除されます (ガベージ コレクションについて心配する必要はありません)。また、可変性と不変性の両方のパフォーマンス上のメリットをすべて享受できます。

于 2009-08-10T07:22:20.413 に答える
32

上記のウィキペディアの定義を使用する場合、実際には String は不変ではありません。

文字列の状態は、構築後に変更されます。hashcode() メソッドを見てください。String はハッシュコード値をローカル フィールドにキャッシュしますが、hashcode() の最初の呼び出しまで計算しません。ハッシュコードのこの遅延評価は、状態が変化する不変オブジェクトとして興味深い位置に String を配置しますが、リフレクションを使用しないと変化したことを観察することはできません。

したがって、不変の定義は、変更されたことを確認できないオブジェクトにする必要があります。

不変オブジェクトが作成された後に状態が変更されたが、誰もそれを見ることができない場合 (リフレクションなしで)、オブジェクトはまだ不変ですか?

于 2009-02-04T17:34:30.590 に答える
24

不変オブジェクトは、プログラムで変更できないオブジェクトです。これらは、マルチスレッド環境や、複数のプロセスがオブジェクトの値を変更 (変更) できるその他の環境に特に適しています。

ただし、明確にするために、 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 は新しいインスタンスを作成せずに変更できるため、可変オブジェクトです。

于 2008-11-10T23:20:11.940 に答える
16

「不変」とは、値を変更できないことを意味します。String クラスのインスタンスがある場合、値を変更するように見えるメソッドを呼び出すと、実際には別の String が作成されます。

String foo = "Hello";
foo.substring(3);
<-- foo here still has the same value "Hello"

変更を保存するには、次のようにする必要があります foo = foo.sustring(3);

コレクションを操作する場合、不変対可変は面白いことがあります。変更可能なオブジェクトを map のキーとして使用し、値を変更するとどうなるかを考えてみてください (ヒント: と について考えてくださいequals) hashCode

于 2008-11-10T23:08:36.317 に答える
13

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()… 2014-03-18

dateAfterTwoYears.toString()… 2016-03-18

于 2016-06-12T15:44:09.123 に答える
8

SCJP Sun Certified Programmer for Java 5 Study Guideの説明がとても気に入っています。

Java をよりメモリ効率的にするために、JVM は「文字列定数プール」と呼ばれる特別なメモリ領域を確保します。コンパイラは、文字列リテラルを検出すると、プールをチェックして、同一の文字列が既に存在するかどうかを確認します。一致が見つかった場合、新しいリテラルへの参照は既存の String に向けられ、新しい String リテラル オブジェクトは作成されません。

于 2008-11-11T00:33:23.663 に答える
8

不変のオブジェクトは、作成後に状態を変更することはできません。

可能な限り不変オブジェクトを使用する主な理由は 3 つあります。これらはすべて、コードに持ち込むバグの数を減らすのに役立ちます。

  • オブジェクトの状態を別の方法で変更できないことがわかっていると、プログラムがどのように機能するかを推論するのがはるかに簡単になります。
  • 不変オブジェクトは自動的にスレッド セーフになるため (安全に公開されている場合)、特定が困難なマルチスレッド バグの原因にはなりません。
  • 不変オブジェクトは常に同じ Hash コードを持つため、HashMap (または同様のもの) のキーとして使用できます。ハッシュ テーブル内の要素のハッシュ コードが変更された場合、テーブル内でそれを見つけようとすると間違った場所を探すことになるため、テーブル エントリは実質的に失われます。これが、String オブジェクトが不変である主な理由です。これらは、HashMap キーとして頻繁に使用されます。

オブジェクトの状態が不変であることがわかっている場合、コードで実行できる他の最適化もいくつかあります。たとえば、計算されたハッシュをキャッシュします。ただし、これらは最適化であるため、それほど興味深いものではありません。

于 2008-11-11T11:05:32.843 に答える
5

1 つの意味は, 値がコンピュータにどのように格納されるかに関係しています. たとえば.Net 文字列の場合, メモリ内の文字列は変更できないことを意味します. 変更していると思うと, 実際には新しい文字列を作成しています.文字列をメモリに格納し、既存の変数 (別の場所にある文字の実際のコレクションへのポインターにすぎません) を新しい文字列にポイントします。

于 2008-11-10T23:11:34.953 に答える
3

不変とは、オブジェクトが作成されると、そのメンバーが変更されないことを意味します。String内容を変更できないため、不変です。例えば:

String s1 = "  abc  ";
String s2 = s1.trim();

上記のコードでは、文字列 s1 は変更されず、別のオブジェクト ( s2) が を使用して作成されましs1た。

于 2008-11-10T23:07:53.400 に答える
3

イミュータブルとは、単純に、変更不可または変更不可を意味します。文字列オブジェクトが作成されると、そのデータまたは状態は変更できません

以下の例を考えてみましょう。

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 で不変である理由です。

于 2018-07-17T07:55:41.113 に答える
2

一度インスタンス化すると、変更できません。インスタンスがハッシュテーブルなどのキーとして使用される可能性があるクラスを考えてみましょう。Java のベスト プラクティスを確認してください。

于 2008-11-10T23:07:04.743 に答える
1

受け入れられた回答がすべての質問に回答するわけではないため。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

于 2020-05-07T02:24:42.953 に答える
-1

不変オブジェクトとは、作成後に変更できないオブジェクトです。典型的な例は文字列リテラルです。

ますます普及するADプログラミング言語は、「不変」キーワードによる「不変性」の概念を持っています。これに関するDr.Dobbの記事を確認してください-http://dobbscodetalk.com/index.php?option=com_myblog&show=Invariant-Strings.html&Itemid= 29。それは問題を完全に説明します。

于 2008-11-11T00:39:05.557 に答える