Java Collection Interface
Das root Collection Interface in Java und der Vertrag, den jede List, Set und Queue davon erbt.
java.util.Collection<E> ist die Wurzel des Teils des Frameworks, der einzelne Elemente enthält — jede List, Set, Queue und Deque implementiert es (die einzige Familie, die dies nicht tut, ist Map, deren Elemente Einträge und keine einzelnen Werte sind). Alles, was man "unabhängig von der Gruppe" tun kann — hinzufügen, entfernen, contains abfragen, iterieren, zählen, in ein Array umwandeln, streamen — ist hier deklariert. Dieses Kapitel beschreibt den Vertrag: die Methoden, auf die man sich bei jeder Collection verlassen kann, die wenigen, die eine Ausnahme werfen können, und das Iterationsmodell, das jede Implementierung erbt.
Die Hierarchie auf einen Blick
Iterable<E>
└── Collection<E>
├── List<E> — ordered, indexed, duplicates allowed
├── Set<E> — no duplicates
│ └── SortedSet<E> → NavigableSet<E>
└── Queue<E> — "next in line"
└── Deque<E> — double-ended queueCollection erweitert Iterable<E>, weshalb jede Collection mit der for-each-Schleife funktioniert. Die beiden Spezialisierungen von Collection — Set und Queue — verfeinern den Vertrag; Map hat eine eigene Wurzel.
Kernmethoden jeder Collection
Unten ist die vollständige Menge der Instanzmethoden auf Collection, nach Zweck gruppiert. Merken Sie sich eher die Kategorien als die Liste — sobald Sie wissen, dass es eine Methode namens removeIf gibt, können Sie sie finden.
Größe und Leerheit
int size()— Elementanzahl.boolean isEmpty()—size() == 0, aber oft schneller.
Hinzufügen
boolean add(E e)— fügt ein Element hinzu. Gibttruezurück, wenn sich die Collection geändert hat. (EinSetgibtfalsefür ein Duplikat zurück.)boolean addAll(Collection<? extends E> c)— fügt jedes Element vonchinzu.
Entfernen
boolean remove(Object o)— entfernt ein Vorkommen vono.boolean removeAll(Collection<?> c)— entfernt jedes Element, das incvorkommt.boolean retainAll(Collection<?> c)— behält nur die Elemente inc(Schnittmenge).boolean removeIf(Predicate<? super E> filter)— entfernt jedes Element, das dem Prädikat entspricht. Die sauberste Methode zum In-Place-Filtern.void clear()— leert die Collection.
Abfragen
boolean contains(Object o)— Mitgliedschaftstest.boolean containsAll(Collection<?> c)— Teilmengentest.
Iteration und Massenansichten
Iterator<E> iterator()— der zugrunde liegende Iterator; wasfor-eachverwendet.Stream<E> stream()/parallelStream()— öffnet einenStreamüber die Elemente.void forEach(Consumer<? super E> action)— vonIterablegeerbt. Die funktionale Iterationsform.
Array-Konvertierung
Object[] toArray()<T> T[] toArray(T[] a)und das neuere<T> T[] toArray(IntFunction<T[]> generator)(Java 11+) — typisiertes Array.
Das ist das gesamte Interface. Jede Liste, jedes Set und jede Queue, der Sie in diesem Teil begegnen werden, ist nur eine andere Implementierung dieser Methoden plus einige eigene.
Gleichheit und equals / hashCode
Collection.equals(Object) ist nicht auf der Wurzel definiert — jedes Sub-Interface legt fest, was Gleichheit für es bedeutet. List erfordert gleiche Elemente in gleicher Reihenfolge; Set erfordert gleiche Elemente unabhängig von der Reihenfolge; Queue definiert equals überhaupt nicht (eine LinkedList-Queue und eine ArrayDeque-Queue mit demselben Inhalt sind nicht gleich, da der Vergleich auf Identität zurückfällt). Vergleichen Sie keine Collections über Familien hinweg und erwarten Sie Symmetrie.
Elemente, die in einer Collection gespeichert sind, müssen ein vernünftiges equals / hashCode haben, wenn contains, remove und (bei hash-basierten Collections) Lookups korrekt funktionieren sollen. Den Vertrag haben wir im Kapitel equals und hashCode behandelt — das ist die Voraussetzung für die Verwendung von Set und Map mit eigenen Klassen.
Optionale Operationen
Einige Collections sind unveränderlich — List.of(1,2,3), Collections.unmodifiableList(list), die von Map.keySet() bei bestimmten Implementierungen zurückgegebenen Views usw. Sie implementieren weiterhin Collection, aber das Aufrufen von add, remove, clear oder einer anderen mutierenden Methode wirft UnsupportedOperationException. Das Javadoc nennt diese "optionale Operationen." Das ist das Nächste, was Java einem Laufzeit-Opt-out für Teile eines Interfaces hat; der Preis ist, dass der Compiler den Fehler nicht abfangen kann — man erfährt es beim ersten Werfen.
Eine sichere Regel: Wenn Sie die Collection nicht selbst erstellt haben, behandeln Sie sie als möglicherweise unveränderlich. Wenn Sie eine veränderliche Kopie benötigen, führen Sie zuerst new ArrayList<>(received) aus.
Iteration: drei Formen, ein zugrunde liegender Mechanismus
Jede Collection unterstützt drei Iterationsstile, und alle drei enden damit, iterator() aufzurufen:
Collection<String> names = List.of("Ada", "Linus", "Grace");
// 1. for-each — the everyday form
for (String n : names) System.out.println(n);
// 2. forEach with a lambda — declarative
names.forEach(System.out::println);
// 3. Iterator — when you need to remove during iteration
Iterator<String> it = names.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.startsWith("L")) it.remove(); // safe; for-each can't do this
}Warum Formen 1 und 3 beide noch relevant sind: Die for-each-Schleife kann die zugrunde liegende Collection nicht modifizieren, ohne eine ConcurrentModificationException zu werfen. Wenn Sie während der Iteration entfernen müssen, greifen Sie auf den expliziten Iterator zurück. Das Kapitel Iterators später in diesem Teil behandelt das Protokoll im Detail.
Mengenalgebra mit Massenoperationen
Die Massenoperationen verwandeln eine Collection in einen Mengenalgebra-Rechner (unabhängig davon, ob es ein Set ist — sie funktionieren auch auf List):
Collection<Integer> a = new ArrayList<>(List.of(1, 2, 3, 4));
Collection<Integer> b = List.of(3, 4, 5);
a.addAll(b); // union (multiset)
a.retainAll(List.of(3, 4)); // intersection
a.removeAll(List.of(3)); // differenceDies ist die sichere, abhängigkeitsfreie Methode, um "nur die Elemente behalten, die auch in b sind" auszudrücken, ohne eine Schleife zu schreiben. Sie modifizieren den Empfänger — wenn Sie ein unveränderliches Ergebnis benötigen, kopieren Sie zuerst.
Ein ausgearbeitetes Beispiel: jede Methode nebeneinander
Das folgende Programm übt jede Kategorie der Collection-Methode gegen dieselbe ArrayList aus, damit Sie sie an einem Ort sehen und den Vertrag beobachten können.
Zwei Dinge sind aus der Ausgabe hervorzuheben:
remove("red")hat nur das erste Vorkommen entfernt — das ist der Vertrag beiCollection. Um jede Übereinstimmung zu entfernen, verwenden SieremoveIf(man sieht es danach, wo jedes Wort länger als vier Zeichen entfernt wird).- Die
UnsupportedOperationExceptionvonfrozen.add("d")ist die "optionale Operationen"-Regel in Aktion.frozenimplementiertCollection, sodass der Aufruf kompiliert. Die Implementierung hat sich entschieden, dies nicht zu unterstützen, und man erfährt es zur Laufzeit.
Was kommt als Nächstes
Collection ist der abstrakte Vertrag. Die erste konkrete Verfeinerung, der Sie begegnen werden, ist diejenige, die Reihenfolge und Indizierung hinzufügt — das List Interface. Dort kommen indizierter Zugriff, Teillisten und reihenfolgebewahrende Operationen ins Spiel.