web-dev-qa-db-ja.com

不変コレクションと変更不可能コレクション

Collections Framework Overview から:

変更操作をサポートしないコレクション(addremove、およびclearなど)は、unmodifiableと呼ばれます。変更不可能なコレクションはmodifiableです。

Collectionオブジェクトの変更が表示されないことをさらに保証するコレクションは、immutableと呼ばれます。不変ではないコレクションはmutableです。

私はその区別を理解できません。
unmodifiableimmutableの違いは何ですか?

152
Cratylus

基本的にunModifiableコレクションはビューであるため、間接的に、変更可能な他の参照から「変更」される可能性があります。また、別のコレクションのreadonly viewとして、ソースコレクションが変更されると、unModifiable Collectionは常に最新の値で表示されます。

ただし、immutableコレクションは、別のコレクションのreadonly copyとして扱うことができ、変更できません。この場合、ソースコレクションが変更されると、不変コレクションは変更を反映しません

この違いを視覚化するためのテストケースを次に示します。

@Test
public void testList() {

    List<String> modifiableList = new ArrayList<String>();
    modifiableList.add("a");

    System.out.println("modifiableList:"+modifiableList);
    System.out.println("--");


    //unModifiableList

    assertEquals(1, modifiableList.size());

    List<String> unModifiableList=Collections.unmodifiableList(
                                        modifiableList);

    modifiableList.add("b");

    boolean exceptionThrown=false;
    try {
        unModifiableList.add("b");
        fail("add supported for unModifiableList!!");
    } catch (UnsupportedOperationException e) {
        exceptionThrown=true;
        System.out.println("unModifiableList.add() not supported");
    }
    assertTrue(exceptionThrown);

    System.out.println("modifiableList:"+modifiableList);
    System.out.println("unModifiableList:"+unModifiableList);

    assertEquals(2, modifiableList.size());
    assertEquals(2, unModifiableList.size());
            System.out.println("--");



            //immutableList


    List<String> immutableList=Collections.unmodifiableList(
                            new ArrayList<String>(modifiableList));

    modifiableList.add("c");

    exceptionThrown=false;
    try {
        immutableList.add("c");
        fail("add supported for immutableList!!");
    } catch (UnsupportedOperationException e) {
        exceptionThrown=true;
        System.out.println("immutableList.add() not supported");
    }
    assertTrue(exceptionThrown);


    System.out.println("modifiableList:"+modifiableList);
    System.out.println("unModifiableList:"+unModifiableList);
    System.out.println("immutableList:"+immutableList);
    System.out.println("--");

    assertEquals(3, modifiableList.size());
    assertEquals(3, unModifiableList.size());
    assertEquals(2, immutableList.size());

}

出力

modifiableList:[a]
--
unModifiableList.add() not supported
modifiableList:[a, b]
unModifiableList:[a, b]
--
immutableList.add() not supported
modifiableList:[a, b, c]
unModifiableList:[a, b, c]
immutableList:[a, b]
--
83
Prashant Bhate

主な違いは、可変コレクションの所有者が他のコードへのコレクションへのアクセスを提供したいが、他のコードがコレクションを変更することを許可しないインターフェースを介してそのアクセスを提供する可能性があることだと思います(その機能を予約している間)所有コードへ)。そのため、コレクションは不変ではありませんが、特定のユーザーはコレクションを変更できません。

Oracleの Java Collection Wrapperチュートリアル には次のように書かれています(強調が追加されています):

変更不可能なラッパーには、次の2つの主な用途があります。

  • 一度構築されたコレクションを不変にする。この場合、バッキングコレクションへの参照を維持しないことをお勧めします。これにより、不変性が完全に保証されます。
  • 特定のクライアントにデータ構造への読み取り専用アクセスを許可します。バッキングコレクションへの参照を保持しますが、ラッパーへの参照を配布します。このようにして、フルアクセスを維持しながら、クライアントは外観を変更することはできません
11
Michael Burr

引用するには Java™チュートリアル

ラップされたコレクションに機能を追加する同期ラッパーとは異なり、変更不可能なラッパーは機能を奪います。特に、コレクションを変更するすべての操作をインターセプトし、UnsupportedOperationExceptionをスローすることにより、コレクションを変更する機能を奪います。変更不可能なラッパーには、次の2つの主な用途があります。

  • 一度構築されたコレクションを不変にする。この場合、バッキングコレクションへの参照を維持しないことをお勧めします。これにより、不変性が完全に保証されます。

  • 特定のクライアントにデータ構造への読み取り専用アクセスを許可します。バッキングコレクションへの参照を保持しますが、ラッパーへの参照を配布します。このようにして、フルアクセスを維持しながら、クライアントは外観を変更できますが、変更はできません。

(エンファシス鉱山)

これは本当にそれを要約しています。

2
Bharath

JDK Unmodifiable*対guava Immutable*について話している場合、実際にはperformanceにも違いがあります。不変コレクションは、not通常のコレクションのラッパー(JDK実装はラッパー)であれば、より高速で、メモリ効率が向上します。 グアバチームを引用

JDKはCollections.unmodifiableXXXメソッドを提供しますが、私たちの意見では、これらは

<...>

  • 非効率:データ構造には、同時変更チェック、ハッシュテーブルの余分なスペースなど、変更可能なコレクションのすべてのオーバーヘッドが残っています
2
Dmide

前述のように、変更不可能なコレクションは不変ではありません。たとえば、変更不可能なコレクションに、他のオブジェクトによって参照される基礎となるデリゲートコレクションがあり、そのオブジェクトがそれを変更する場合です。

不変に関しては、十分に定義されていません。ただし、一般的には、オブジェクトは「変更されない」ことを意味しますが、再帰的に定義する必要があります。たとえば、インスタンス変数がすべてプリミティブであり、メソッドがすべて引数を含まず、プリミティブを返すクラスで不変を定義できます。その後、メソッドは、インスタンス変数を不変にし、すべてのメソッドに不変で不変の値を返す引数を再帰的に許可します。メソッドは、時間の経過とともに同じ値を返すことが保証されている必要があります。

それができると仮定すると、スレッドセーフという概念もあります。 そして、不変(または時間とともに変化しない)はスレッドセーフも意味すると考えるようになるかもしれません。ただしそうではありませんそしてそれが私が作っている主なポイントですここでは、他の回答ではまだ言及されていません。常に同じ結果を返すが、スレッドセーフではない不変オブジェクトを作成できます。これを見るために、追加と削除を長期にわたって維持することで不変のコレクションを構築すると仮定します。現在、不変のコレクションは、内部コレクション(時間の経過とともに変化する可能性があります)を調べ、コレクションの作成後に追加または削除された要素を(内部的に)追加および削除することにより、要素を返します。明らかに、コレクションは常に同じ要素を返しますが、値を変更しないという理由だけでスレッドセーフではありません。

これで、スレッドセーフで変更されないオブジェクトとして不変を定義できます。一般にそのようなクラスにつながる不変クラスを作成するためのガイドラインがありますが、たとえば、上記の「スナップショット」コレクションの例で説明したように、スレッドの安全性に注意を払う不変クラスを作成する方法があるかもしれないことに留意してください。

1
dan b