In der Vergangenheit habe ich gesagt, eine Sammlung sicher zu kopieren, um Folgendes zu tun:
public static void doThing(List<String> strs) {
List<String> newStrs = new ArrayList<>(strs);
oder
public static void doThing(NavigableSet<String> strs) {
NavigableSet<String> newStrs = new TreeSet<>(strs);
Aber sind diese "Kopier" -Konstruktoren, ähnliche statische Erstellungsmethoden und Streams, wirklich sicher und wo sind die Regeln angegeben? Mit sicher meine ich die grundlegenden semantischen Integritätsgarantien , die von der Java-Sprache und den Sammlungen angeboten werden, die gegen einen böswilligen Anrufer erzwungen werden, vorausgesetzt, sie werden von einem vernünftigen unterstützt SecurityManager
und es gibt keine Fehler.
Ich bin glücklich mit dem Verfahren werfen ConcurrentModificationException
, NullPointerException
, IllegalArgumentException
, ClassCastException
, usw., oder vielleicht sogar hängen.
Ich habe String
als Beispiel für ein unveränderliches Typargument gewählt. Für diese Frage interessieren mich keine tiefen Kopien für Sammlungen veränderlicher Typen, die ihre eigenen Fallstricke haben.
(Um klar zu sein, habe ich mir den OpenJDK-Quellcode angesehen und habe eine Antwort auf ArrayList
und TreeSet
.)
NavigableSet
und andere Comparable
basierte Sammlungen können manchmal erkennen, ob eine Klasse nicht compareTo()
korrekt implementiert ist , und eine Ausnahme auslösen. Es ist ein bisschen unklar, was Sie unter nicht vertrauenswürdigen Argumenten verstehen. Du meinst, ein Übeltäter bastelt eine Sammlung von schlechten Saiten und wenn du sie in deine Sammlung kopierst, passiert etwas Schlimmes? Nein, das Sammlungs-Framework ist ziemlich solide, es gibt es seit 1.2.
HashSet
(und alle anderen Hashing-Sammlungen im Allgemeinen). Dies hängt von der Richtigkeit / Integrität der hashCode
Implementierung der Elemente ab TreeSet
und PriorityQueue
hängt von der ab Comparator
(und Sie können nicht einmal Erstellen Sie eine äquivalente Kopie, ohne den benutzerdefinierten Komparator zu akzeptieren (falls vorhanden), und EnumSet
vertrauen Sie auf die Integrität des bestimmten enum
Typs, der nach der Kompilierung nie überprüft wird, sodass eine Klassendatei, die nicht mit javac
oder in Handarbeit erstellt wurde, diese untergraben kann.
new TreeSet<>(strs)
wo strs
ist ein NavigableSet
. Dies ist keine Massenkopie, da das Ergebnis TreeSet
den Komparator der Quelle verwendet, der sogar erforderlich ist, um die Semantik beizubehalten. Wenn Sie nur die enthaltenen Elemente verarbeiten können, toArray()
ist dies der richtige Weg. Es wird sogar die Iterationsreihenfolge beibehalten. Wenn Sie mit "Element nehmen, Element validieren, Element verwenden" einverstanden sind, müssen Sie nicht einmal eine Kopie erstellen. Die Probleme beginnen, wenn Sie alle Elemente überprüfen und anschließend alle Elemente verwenden möchten. Dann können Sie einer TreeSet
Kopie mit benutzerdefiniertem Komparator nicht vertrauen
checkcast
für jedes Element erfolgt toArray
mit einem bestimmten Typ. Wir enden immer damit. Die generischen Sammlungen kennen nicht einmal ihren tatsächlichen Elementtyp, sodass ihre Kopierkonstruktoren keine ähnliche Funktionalität bereitstellen können. Natürlich können Sie jede Überprüfung auf die richtige vorherige Verwendung verschieben, aber dann weiß ich nicht, worauf Ihre Fragen abzielen. Sie benötigen keine "semantische Integrität", wenn Sie unmittelbar vor der Verwendung von Elementen prüfen und fehlschlagen können.