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


170

以下からのコレクションフレームワークの概要

変更操作をサポートしていないコレクションは、(のようなaddremoveclear)と呼ばれている変更不可能変更不可でないコレクションは変更可能です。

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

区別がつかない。ここで変更不可不変の
違いは何ですか?

回答:


207

多くの場合、変更不可能なコレクションは、他のコードがにアクセスできる変更可能なコレクションのラッパーです。そうしながら、あなたはあなたが唯一の変更不可能なコレクションへの参照を持っている場合、それを変更することはできません、あなたは変わらない内容に依存することはできません。

不変コレクションは、その保証は何も任意のより多くのコレクションを変更することはできません。変更可能なコレクションをラップする場合は、他のコードがその変更可能なコレクションにアクセスできないようにします。コードがコレクションに含まれる参照を含むオブジェクトを変更することはできませんが、オブジェクト自体は変更可能である可能性があることに注意してくださいStringBuilder

基本的に、違いは、他のコードが背後でコレクションを変更できるかどうかです。


63
不変コレクションは、も変更できないことを保証するものではありません。これは、コレクション自体が(ラップではなくコピーによって)変更できないことを確認するだけです。コレクションに存在するオブジェクトは引き続き変更することができ、それらは保証されません。
Hiery Nomus 2012年

8
@HieryNomus:何も変更できないとは言わなかったことに注意してください-コレクションは何も変更できないと言いました。
Jon Skeet、2012年

1
わかりました、それを誤解しているかもしれません;)しかし、それを明確にすることは良いことです。
Hiery Nomus 2012年

5
だから、あなたが言っていること。真の不変性のためには、不変タイプのアイテムを含む不変コレクションが必要です。
エヴァンプレイス2014

1
@savanibharat:変更可能なコードパスがあるかどうかによって異なりlistます。何かが後で呼び出すことができる場合はlist.add(10)、次にcollその変更が反映されますので、いや、私はそれが不変で呼び出すことはありません。
Jon Skeet

92

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

ただし、immutableコレクションは別のコレクションの読み取り専用コピーとして扱うことができ、変更することはできません。この場合、ソースコレクションが変更されても、不変コレクションには変更が反映されません

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

@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]
--

違いがわかりません。Immutableの違いを教えてください。ImmutableとUnmodifiableの両方がエラーをスローしており、addがサポートされていないことがわかります。ここで何か不足していますか?
AKS

2
@AKSリストに「C」を加えた後、最後の3つのリストエントリの出力を参照してください。両方の大きさは、一方でmodifiableListunModifiableList増加しているimmutableListサイズが変更されていない
のPrashant Bhateを

1
ああ!とった!:) ..したがって、ここでは、modifiableListの変更を使用してunmodifableListを変更しましたが、ImmutableListは変更できません。しかし、ImmutableListも変更できるのと同じように、ここではクライアントはImmutableList参照のみにアクセスでき、ImmutableListが作成されたmodifiableListへの参照はクライアントに公開されません。正しい?
AKS

1
はい、new ArrayList<String>(modifiableList)immutableList への参照がないため変更できません
Prashant Bhate 2013

@PrashantBhateこんにちは、への参照が存在しないnew ArrayList<String>(modifiableList)ためのはnew?ありがとう。
Unheilig 2014年

11

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

OracleのJava Collection Wrapperチュートリアルでは、これを強調しています(強調を追加)。

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

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

3

JDK Unmodifiable*とグアバについて話をしている場合Immutable*、実際には違いもパフォーマンスにあります。不変コレクションは、通常のコレクションのラッパーではない場合(JDK実装はラッパー)、高速でメモリ効率も高くなります。 グアバチームの引用

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

<...>

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

パフォーマンスについて考えると、変更不可能なラッパーがコレクションをコピーしないことも考慮する必要があります。ここで、グアバで使用されている不変バージョンと、現在のjdk9 +では、たとえばList.of(...)実際に2回コピーされます。
benez 2018

2

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

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

  • コレクションが作成された後でコレクションを不変にすること。この場合、バッキングコレクションへの参照を維持しないことをお勧めします。これは絶対に不変性を保証します。

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

(強調鉱山)

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


1

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

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

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

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


1

Java™チュートリアルでは、次のように述べています。

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

コレクションが作成された後でコレクションを不変にすること。この場合、バッキングコレクションへの参照を維持しないことをお勧めします。これは絶対に不変性を保証します。

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

違いを理解するのに十分な説明だと思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.