Unveränderliche Java-Sammlungen

115

Aus der Java 1.6 Collection Framework-Dokumentation :

Kollektionen , die (wie etwa keine Änderungsoperationen unterstützen add, removeund clear) , werden als nicht änderbar . [...] Sammlungen, die zusätzlich garantieren, dass keine Änderung des Sammlungsobjekts jemals sichtbar wird, werden als unveränderlich bezeichnet .

Das zweite Kriterium verwirrt mich ein bisschen. Welche Änderungen werden in der zweiten Zeile erwähnt, da die erste Sammlung nicht geändert werden kann und vorausgesetzt, dass die ursprüngliche Sammlungsreferenz entfernt wurde? Bezieht es sich auf die Änderungen in den Elementen in der Sammlung, dh auf den Status der Elemente?

Zweite Frage:
Damit eine Sammlung unveränderlich ist, wie werden die angegebenen zusätzlichen Garantien bereitgestellt? Wenn der Status eines Elements in der Sammlung von einem Thread aktualisiert wird, reicht es für die Unveränderlichkeit aus, dass diese Aktualisierungen im Status auf dem Thread, der die unveränderliche Sammlung enthält, nicht sichtbar sind?

Wie geht man vor, um eine unveränderliche Sammlung unveränderlich zu machen?

Bhaskar
quelle
Im Allgemeinen (insbesondere in funktionalen Sprachen) kann sich die unveränderliche (auch als persistent bezeichnete) Sammlung dahingehend ändern, dass Sie einen neuen Status dieser Sammlung erhalten können, der alte Status jedoch weiterhin über andere Links verfügbar ist. Beispielsweise newCol = oldCol.add("element")wird eine neue Sammlung erstellt, die eine Kopie der alten mit einem weiteren Element ist, und alle Verweise auf die oldColwerden weiterhin auf dieselbe unveränderte alte Sammlung verweisen.
Freund

Antworten:

154

Nicht modifizierbare Sammlungen sind normalerweise schreibgeschützte Ansichten (Wrapper) anderer Sammlungen. Sie können sie nicht hinzufügen, entfernen oder löschen, aber die zugrunde liegende Sammlung kann sich ändern.

Unveränderliche Sammlungen können überhaupt nicht geändert werden - sie verpacken keine andere Sammlung - sie haben ihre eigenen Elemente.

Hier ist ein Zitat von Guaven ImmutableList

Im Gegensatz zu Collections.unmodifiableList(java.util.List<? extends T>)einer Ansicht einer separaten Sammlung, die sich noch ändern kann, ImmutableListenthält eine Instanz von ihre eigenen privaten Daten und wird sich niemals ändern.

Um eine unveränderliche Sammlung aus einer veränderlichen Sammlung herauszuholen, müssen Sie ihre Elemente in die neue Sammlung kopieren und alle Vorgänge nicht zulassen.

Bozho
quelle
@ Bhaskar - siehe meinen letzten Absatz.
Bozho
1
Wenn die Sammlung, die in eine andere nicht veränderbare Sammlung verpackt ist, keine anderen Verweise darauf enthält, ist das Verhalten der nicht veränderbaren Sammlung dann genau dasselbe wie bei einer unveränderlichen Sammlung?
Bhaskar
1
@Bozho, Wenn kein Verweis auf die Hintergrundsammlung angegeben wird und nur ein nicht modifizierbarer Sammlungs-Wrapper-Verweis angegeben wird, können Sie ihn auf keinen Fall ändern. Warum sagst du dann "Du kannst nicht sicher sein"? Weist es auf ein Szenario hin, in dem ein Thread oder irgendwie geändert wird, weil die Backing-Sammlung geändert werden kann?
AKS
@AKS: Wenn eine Sammlung eingeschlossen wird unmodifiableList, kann Code, der nur einen Verweis auf diese Liste erhält, diese nicht ändern, aber jeder Code, der einen Verweis auf die ursprüngliche Liste hatte und diesen vor dem Erstellen des Wrappers ändern konnte, wird weiterhin geändert in der Lage sein, dies später zu tun. Wenn der Code, der die ursprüngliche Liste erstellt hat, weiß, was mit jeder Referenz passiert ist, die jemals existiert hat, und weiß, dass keiner von ihnen in die Hände von Code fällt, der die Liste ändern könnte, kann er wissen, dass die Liste niemals sein wird geändert. Wenn die Referenz jedoch von außerhalb des Codes empfangen wurde ...
Supercat
... es gibt keine Möglichkeit für den unmodifiableListCode, der ihn verwendet, zu wissen, ob oder wie sich die umschlossene Sammlung ändern könnte.
Supercat
86

Der Unterschied besteht darin, dass Sie keinen Verweis auf eine unveränderliche Sammlung haben können, die Änderungen zulässt. Nicht modifizierbare Sammlungen können durch diese Referenz nicht modifiziert werden , aber ein anderes Objekt kann auf dieselben Daten verweisen, über die es geändert werden kann.

z.B

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);
Simon Nickerson
quelle
1
Welche verschiedenen Ursachen können gegebenenfalls zu einer Änderung in einer Sammlung führen, die nicht geändert werden kann, sofern die ursprüngliche Sammlungsreferenz nicht zum Ändern der zugrunde liegenden Sammlung verwendet wird?
Bhaskar
21
Collection<String> c1 = new ArrayList<String>();
c1.add("foo");
Collection<String> c2 = Collections.unmodifiableList(c1);

c1ist veränderlich (dh weder unveränderlich noch unveränderlich ).
c2ist nicht änderbar : es selbst nicht, verändert, aber wenn ich später ändern c1dann , dass wird sich ändern in sichtbar sein c2.

Dies liegt daran, dass c2es sich lediglich um einen Wrapper handelt c1und nicht wirklich um eine unabhängige Kopie. Guava bietet die ImmutableListSchnittstelle und einige Implementierungen. Diese arbeiten, indem sie tatsächlich eine Kopie der Eingabe erstellen (es sei denn, die Eingabe ist eine unveränderliche Sammlung für sich).

Zu Ihrer zweiten Frage:

Die Veränderbarkeit / Unveränderlichkeit einer Sammlung hängt nicht von der Veränderlichkeit / Unveränderlichkeit der darin enthaltenen Objekte ab. Das Ändern eines in einer Sammlung enthaltenen Objekts gilt für diese Beschreibung nicht als "Änderung der Sammlung". Wenn Sie eine unveränderliche Sammlung benötigen, möchten Sie normalerweise auch, dass sie unveränderliche Objekte enthält.

Joachim Sauer
quelle
2
@ John: Nein, das ist es nicht. Zumindest nicht gemäß der vom OP zitierten Definition.
Joachim Sauer
@ JohnVint, wirklich? Ich würde es nicht glauben, da ich mit c1 immer noch Elemente hinzufügen kann und diese Hinzufügung für c2 sichtbar ist. Bedeutet das nicht, dass es nicht unveränderlich ist?
Bhaskar
Nun, ich denke, wenn c1 entkommen kann, wäre es nicht unveränderlich, aber Unveränderlichkeit bezieht sich auch auf den Inhalt innerhalb der Sammlung. Ich bezog mich eher auf eine nicht veränderbare Sammlung von java.util.Date's
John Vint
1
@Vint: "Wenn c1nicht entkommen" ist keine Überlegung, die irgendwo in der Spezifikation unterschieden wird. Wenn Sie die Sammlung meiner Meinung nach so verwenden können , dass sie nicht unveränderlich ist, sollten Sie sie meiner Meinung nach immer als nicht unveränderlich betrachten.
Joachim Sauer
1
Lassen Sie mich die Definition zitieren: "Sammlungen, die zusätzlich garantieren ..." (Hervorhebung von mir). Collection.unmodifiableList() kann das nicht garantieren, weil es nicht garantieren kann, dass sein Argument nicht entgeht. Guave erzeugt ImmutableList.of immer eine unveränderliche List, auch wenn Sie ihre Argumente entkommen lassen.
Joachim Sauer
17

Jetzt verfügt Java 9 über Factory-Methoden für unveränderliche Listen, Mengen, Karten und Map.Entry.

In Java SE 8 und früheren Versionen können wir Dienstprogramme der Collections-Klasse wie unmodizableXXX verwenden, um unveränderliche Collection-Objekte zu erstellen.

Diese Collections.unmodizableXXX-Methoden sind jedoch sehr langwierig und ausführlich. Um diese Mängel zu beheben, hat Oracle Corp. den Schnittstellen List, Set und Map einige Dienstprogrammmethoden hinzugefügt.

Jetzt in Java 9: ​​- List- und Set-Schnittstellen verfügen über "of ()" - Methoden, um eine leere oder nicht leere Immutable List oder Set-Objekte zu erstellen, wie unten gezeigt:

Beispiel für eine leere Liste

List immutableList = List.of();

Beispiel für eine nicht leere Liste

List immutableList = List.of("one","two","three");
Opster Elasticsearch Pro-Vijay
quelle
2
Und in Java 10 List.copyOfund Set.copyOfwurden hinzugefügt, die es ermöglichen, eine nicht veränderbare Kopie einer Liste / eines Satzes zu erstellen oder die angegebene Sammlung zurückzugeben, wenn sie bereits nicht veränderbar ist, siehe JDK-8191517
Marcono1234
6

Ich glaube, der Punkt hier ist, dass selbst wenn eine Sammlung nicht modifizierbar ist, dies nicht sicherstellt, dass sie sich nicht ändern kann. Nehmen Sie zum Beispiel eine Sammlung, die Elemente entfernt, wenn sie zu alt sind. Nicht modifizierbar bedeutet nur, dass das Objekt, das die Referenz enthält, diese nicht ändern kann, nicht, dass es sich nicht ändern kann. Ein wahres Beispiel hierfür ist die Collections.unmodifiableListMethode. Es wird eine nicht veränderbare Ansicht einer Liste zurückgegeben. Die an diese Methode übergebene Listenreferenz kann weiterhin geändert werden, sodass die Liste von jedem Inhaber der übergebenen Referenz geändert werden kann. Dies kann zu ConcurrentModificationExceptions und anderen schlechten Dingen führen.

Unveränderlich bedeutet, dass die Sammlung in keiner Weise geändert werden kann.

Zweite Frage: Eine unveränderliche Sammlung bedeutet nicht, dass sich die in der Sammlung enthaltenen Objekte nicht ändern, sondern dass sich die Anzahl und Zusammensetzung der darin enthaltenen Objekte in dieser Sammlung nicht ändert. Mit anderen Worten, die Referenzliste der Sammlung ändert sich nicht. Dies bedeutet nicht, dass sich die Interna des Objekts, auf das verwiesen wird, nicht ändern können.

John B.
quelle
Gut !! Wenn ich also einen Wrapper über "Nicht modifizierbare Sammlung" erstelle und die Referenz für modifizierbare Sammlungen als privat bezeichne, kann ich sicher sein, dass sie unveränderlich ist. richtig?
AKS
Wenn ich Ihre Frage verstehe, lautet die Antwort nein. Das Umschließen des Unmodifizierbaren schützt es nicht davor, dass die Sammlung an das unmodifiableListModified übergeben wird. Wenn Sie dies tun möchten, verwenden Sie ImmutableList.
John B
0

Pure4J unterstützt das, wonach Sie suchen , auf zwei Arten.

Erstens enthält es eine @ImmutableValueAnmerkung, sodass Sie eine Klasse mit Anmerkungen versehen können, um zu sagen, dass sie unveränderlich ist. Es gibt ein Maven-Plugin, mit dem Sie überprüfen können, ob Ihr Code tatsächlich unveränderlich ist (Verwendung von finalusw.).

Zweitens werden die persistenten Sammlungen von Clojure (mit zusätzlichen Generika) bereitgestellt und sichergestellt, dass die den Sammlungen hinzugefügten Elemente unveränderlich sind. Die Leistung dieser ist anscheinend ziemlich gut. Sammlungen sind alle unveränderlich, implementieren jedoch Java-Sammlungsschnittstellen (und Generika) zur Überprüfung. Mutation gibt neue Sammlungen zurück.

Haftungsausschluss: Ich bin der Entwickler davon

Rob Moffat
quelle