Java不変コレクション


115

以下からのJava 1.6コレクションフレームワークのドキュメント

任意の変更操作を(のようなサポートしていないコレクションはaddremoveclear)と呼ばれている変更不可能。[...]さらに、Collectionオブジェクトの変更が表示されないことを保証するコレクションは、不変と呼ばれます。

2番目の基準は少し混乱します。最初のコレクションは変更できず、元のコレクション参照が破棄されていると仮定すると、2行目で参照されている変更は何ですか?それはコレクションに保持されている要素の変化、つまり要素の状態を参照していますか?

2番目の質問:
コレクションを不変にするには、指定された追加の保証人をどのように提供しますか?コレクション内の要素の状態がスレッドによって更新される場合、状態のそれらの更新が不変のコレクションを保持するスレッドに表示されないことは不変性にとって十分ですか?

コレクションを不変にするには、指定された追加の保証を提供するにはどうすればよいですか?


一般に(特に関数型言語では)、不変(永続的)コレクションは、このコレクションの新しい状態を取得できるという意味で変更される可能性がありますが、同時に古い状態は他のリンクから引き続き利用できます。たとえば、newCol = oldCol.add("element")は1つ以上の要素を持つ古いもののコピーである新しいコレクションを生成し、へのすべての参照oldColは同じ変更されていない古いコレクションを引き続き指します。
ffriend

回答:


154

変更不可能なコレクションは、通常、他のコレクションの読み取り専用ビュー(ラッパー)です。それらを追加、削除、またはクリアすることはできませんが、基になるコレクションは変更される可能性があります。

不変コレクションはまったく変更できません-別のコレクションをラップすることはありません-独自の要素を持っています。

ここにグアバの引用があります ImmutableList

Collections.unmodifiableList(java.util.List<? extends T>)まだ変更可能な個別のコレクションのビューであるとは異なり、のインスタンスにImmutableListは独自のプライベートデータが含まれており、変更されることはありません。

したがって、基本的に、変更可能なコレクションから不変のコレクションを取得するには、その要素を新しいコレクションにコピーし、すべての操作を禁止する必要があります。


@バスカー-私の最後の段落を参照してください。
Bozho

1
別の変更不可能なコレクション内にラップされたコレクションが他の参照を持たない場合、変更不可能なコレクションの動作は不変のコレクションとまったく同じですか?
Bhaskar、2011年

1
@Bozho、バッキングコレクションへの参照が提供されておらず、変更不可能なコレクションラッパー参照のみが提供されている場合、それを変更する方法はありません。では、なぜ「わからない」と言うのですか?それは、バッキングコレクションが変更可能であるために、いくつかのスレッドまたは何らかの方法で変更されるシナリオを指しますか?
AKS

@AKS:コレクションがでラップされている場合、unmodifiableListそのリストへの参照のみを受け取るコードはそれを変更できませんが、元のリストへの参照があり、ラッパーが作成される前に変更できるコードは引き続き後でそうすることができます。元のリストを作成したコードが、これまでに存在したすべての参照に何が起こったかを知っていて、それらのどれもリストを変更する可能性のあるコードの手に渡らないことを知っている場合、リストは決してないことを知ることができます。変更されました。ただし、参照が外部コードから受信された場合...
supercat

unmodifiableListラップされたコレクションが変更されるかどうか、またはどのように変更されるかを知るための方法も、それを使用するコードもありません。
スーパーキャット2013

86

違いは、変更を許可する不変のコレクションへの参照を持つことができないことです。変更不可能なコレクションは、その参照を通じて変更できませんを通じて変更できませんが、他のオブジェクトは、変更可能な同じデータを指す場合があります。

例えば

List<String> strings = new ArrayList<String>();
List<String> unmodifiable = Collections.unmodifiableList(strings);
unmodifiable.add("New string"); // will fail at runtime
strings.add("Aha!"); // will succeed
System.out.println(unmodifiable);

1
元のコレクション参照が基になるコレクションの変更に使用されていない場合、変更できないコレクションに変更をもたらす可能性があるさまざまな原因は何ですか?
バスカー

21
Collection<String> c1 = new ArrayList<String>();
c1.add("foo");
Collection<String> c2 = Collections.unmodifiableList(c1);

c1変更可能な(つまり、どちらも変更不可能でも不変)。
c2変更できません。それ自体は変更できませんが、後で変更するとc1その変更がに表示されc2ます。

これはc2単にのラッパーでc1あり、実際には独立したコピーではないためです。GuavaはImmutableListインターフェースといくつかの実装を提供します。これらは実際に入力のコピーを作成することで機能します(入力がそれ自体で不変のコレクションでない限り)。

2番目の質問について:

コレクションの可変性/不変性は、そこに含まれるオブジェクトの可変性/不変性に依存しませ。コレクションに含まれるオブジェクトの変更は、この説明の「コレクションの変更」としてカウントされませ。もちろん、不変コレクションが必要な場合は、通常、不変オブジェクトを含めることもできます。


2
@ジョン:いいえ、そうではありません。少なくともOPによって引用された定義に従っていない。
ヨアヒムザウアー

@JohnVint、本当に?c1を使用しても要素を追加できるので、私はそうは思わないでしょう。この追加はc2に表示されます。それは不変ではないという意味ですか?
バスカー

まあ、c1がエスケープできるとしたら、それは不変ではないでしょうが、不変性はコレクション内のコンテンツも指します。私は、java.util.DateのunmodifiableCollectionを参照することでした
John Vint

1
@Vint:「c1エスケープしない場合」は、仕様のどこでも区別される考慮事項ではありません。私の意見では、場合、あなたがすることができ、それが非不変作る方法でコレクションを使用し、その後、次のことを行う必要があり、常にそれが非不変考えます。
ヨアヒムザウアー

1
定義を引用させてください:「さらに保証するコレクション...」(強調は私のものです)。Collection.unmodifiableList() することはできません、それはその引数が脱出しないことを保証することはできませんので、それを保証します。グアバは、その引数をエスケープさせても、ImmutableList.of 常に不変を生成Listします。
Joachim Sauer、

17

現在、Java 9には、不変リスト、セット、マップ、およびMap.Entryのファクトリメソッドがあります。

Java SE 8以前のバージョンでは、unmodifiableXXXなどのコレクションクラスユーティリティメソッドを使用して、不変コレクションオブジェクトを作成できます。

ただし、これらのCollections.unmodifiableXXXメソッドは非常に退屈で冗長なアプローチです。これらの欠点を克服するために、Oracle CorpはList、Set、Mapインターフェースにいくつかのユーティリティメソッドを追加しました。

現在、Java 9 では、ListおよびSetインターフェースに「of()」メソッドがあり、以下に示すように、空または空でない不変リストまたはセットオブジェクトを作成します。

空のリストの例

List immutableList = List.of();

空でないリストの例

List immutableList = List.of("one","two","three");

2
そして、Javaの10にList.copyOfし、Set.copyOfリスト/セットの変更不可能なコピーを作成することができ、またはそれはすでに変更不可能である場合は、指定されたコレクションを返すが追加されました、見JDK-8191517
Marcono1234

6

ここでのポイントは、コレクションが変更不可であっても、変更できないことを保証するものではないということです。たとえば、要素が古すぎる場合に要素を削除するコレクションを考えてみましょう。変更不可とは、参照を保持しているオブジェクトが変更できないことを意味し、変更できないことを意味します。これの真の例はCollections.unmodifiableListメソッドです。リストの変更不可能なビューを返します。このメソッドに渡されたList参照は引き続き変更可能であるため、渡された参照の任意の所有者がリストを変更できます。これにより、ConcurrentModificationExceptionsやその他の問題が発生する可能性があります。

不変、つまりコレクションを変更することはできません。

2番目の質問:不変コレクションは、コレクションに含まれるオブジェクトが変更されないことを意味するのではなく、コレクションが保持するオブジェクトの数と構成が変更されないだけです。つまり、コレクションの参照リストは変更されません。これは、参照されるオブジェクトの内部が変更できないことを意味するものではありません。


いいね!したがって、Unmodifiable Collectionのラッパーを作成し、変更可能なコレクションの参照をプライベートとして作成すると、それが不変であることを確認できます。正しい?
AKS

私があなたの質問を理解したら、答えはノーです。Unmodifiableをラップしても、コレクションがunmodifiableList変更に渡されるのを防ぐことはできません。これを行いたい場合は、を使用してくださいImmutableList
John B

0

Pure4Jは、2つの方法であなたが求めるものをサポートします。

まず、@ImmutableValueアノテーションを提供するので、クラスにアノテーションを付けて、クラスが不変であると言うことができます。コードが実際に不変であることを確認できるようにするMavenプラグインがあります(使用finalなど)。

2つ目は、Clojureからの永続的なコレクションを提供し(ジェネリックを追加)、コレクションに追加された要素が不変であることを保証します。これらのパフォーマンスは明らかにかなり良いです。コレクションはすべて不変ですが、検査のためにJavaコレクションインターフェイス(およびジェネリック)を実装します。突然変異は新しいコレクションを返します。

免責事項:私はこれの開発者です

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