Java Collections Utility-Klasse
Die Collections-Klasse in Java nutzen: sortieren, suchen, umkehren, mischen und Collections kapseln.
java.util.Collections ist die Standardbibliothek mit statischen Hilfsmethoden, die auf Collections operieren. Stellen Sie sich diese Klasse wie java.util.Arrays vor: eine final-Klasse ohne Instanzzustand, nur mit statischen Methoden. Man schreibt nie new Collections() — sondern Collections.sort(list), Collections.shuffle(list), Collections.unmodifiableMap(map).
Es ist leicht, die Klasse mit der benachbarten Schnittstelle zu verwechseln: Collection<E> (Schnittstelle, mit großem C und ohne s) ist der Obertyp von List, Set und Queue; Collections (Klasse, Plural) ist der Werkzeugkasten. Die Klasse implementiert die Schnittstelle nicht — sie operiert lediglich auf Collections, die dies tun.
Eine geführte Tour durch den Werkzeugkasten
Die Methoden gliedern sich in sechs Themenbereiche. Wir behandeln jeden davon; die nächsten zwei Kapitel vertiefen das Sortieren und Suchen im Detail.
1. Ordnen und Umordnen
Collections.sort(list); // natural order — requires Comparable
Collections.sort(list, comparator); // custom comparator
Collections.reverse(list); // in place
Collections.shuffle(list); // pseudo-random permutation
Collections.shuffle(list, new Random(42)); // deterministic shuffle with a seeded RNG
Collections.rotate(list, 2); // [a,b,c,d,e] → [d,e,a,b,c]
Collections.swap(list, 0, list.size() - 1); // swap two indicessort ist ein stabiles Mergesort — gleiche Elemente behalten ihre relative Reihenfolge. shuffle führt einen Fisher-Yates-Mischvorgang durch, der gleichmäßig zufällig ist, wenn es der RNG ist. rotate ist genau das Richtige, wenn man meint „alles um N Positionen verschieben, mit Umbruch an den Enden". reverse, swap und rotate verändern die Liste in-place; keine von ihnen gibt etwas Nützliches zurück.
2. Suchen
int i = Collections.binarySearch(sortedList, key); // O(log n) — list must be sorted
int j = Collections.binarySearch(sortedList, key, comparator);
T max = Collections.max(coll);
T min = Collections.min(coll, comparator);
int n = Collections.frequency(coll, target); // how many times target appears
boolean disjoint = Collections.disjoint(a, b); // no element in common?binarySearch hat ein eigenes Kapitel — kurzgefasst: die Liste muss bereits in derselben Reihenfolge sortiert sein, die die Suche verwendet, und ein negativer Rückgabewert bedeutet „nicht gefunden, aber Sie können den Einfügepunkt als -result - 1 berechnen."
3. Füllen, Kopieren, Ersetzen
Collections.fill(list, "x"); // overwrite every slot with "x"
Collections.copy(dest, src); // copy src into dest; dest.size() must be ≥ src.size()
Collections.replaceAll(list, "old", "new"); // returns true if anything changed
Collections.nCopies(5, "x"); // immutable list with "x" 5 times
Collections.singleton(value); // immutable Set of one
Collections.singletonList(value); // immutable List of one
Collections.singletonMap(k, v); // immutable Map of one entry
Collections.emptyList(); Collections.emptyMap(); Collections.emptySet();Die Fabriken für leere/Singleton/nCopies-Instanzen geben gecachte, unveränderliche Instanzen zurück — sie allozieren nicht pro Aufruf. Das ist eine kleine, kostenlose Optimierung, wenn man eine bekannte leere oder bekannte kleine Collection benötigt.
4. Synchronisierte Wrapper (hauptsächlich historisch)
List<String> lockedList = Collections.synchronizedList(new ArrayList<>());
Map<String, Int> lockedMap = Collections.synchronizedMap(new HashMap<>());
Set<String> lockedSet = Collections.synchronizedSet(new HashSet<>());Diese kapseln eine Collection so, dass jede Methode eine Sperre auf dem Wrapper erwirbt. Der gleiche Vorbehalt wie bei Hashtable gilt: zusammengesetzte Operationen sind weiterhin racy, und Iteratoren müssen explizit in synchronized (wrapper) { ... }-Blöcken eingeschlossen werden:
synchronized (lockedList) {
for (String s : lockedList) { ... } // safe: holds the lock for the whole walk
}In modernem Code verwendet man stattdessen ConcurrentHashMap, CopyOnWriteArrayList und ConcurrentSkipListSet. Die synchronisierten Wrapper existieren, um eine nicht-threadsichere API nachträglich threadsicher zu machen, wenn nichts anderes passt.
5. Nicht-veränderliche Wrapper
List<String> frozen = Collections.unmodifiableList(mutableList);
Set<String> frozenS = Collections.unmodifiableSet(mutableSet);
Map<K, V> frozenM = Collections.unmodifiableMap(mutableMap);Diese kapseln eine Collection so, dass Mutationsmethoden UnsupportedOperationException werfen. Die originale Collection ist weiterhin veränderlich — der Wrapper ist eine schreibgeschützte Sicht. Änderungen über das Original sind durch die Sicht sichtbar. Das ist ein wesentlicher Unterschied zu den Fabriken List.of(...) / Set.of(...) / Map.of(...), die vollständig unveränderliche Collections mit eigenem Speicher erzeugen. Das nächste Kapitel vergleicht beide.
6. Einzelelement- und typsichere Sichten
List<Object> objects = new ArrayList<>();
List<String> safe = Collections.checkedList(objects, String.class);
safe.add("ok"); // fine
((List) safe).add(42); // throws ClassCastException immediately, not latercheckedList, checkedSet, checkedMap installieren eine Laufzeit-Typprüfung bei jeder Einfügeoperation. Nützlich in Legacy-Code, der generische Collections durch Object-typisierte APIs übergibt — der Wrapper schlägt laut an der Stelle der Einfügung fehl, statt viel später beim Abrufen.
Einige kleine, aber wertvolle Methoden
Collections.disjoint(a, b)gibttruezurück, wenn kein Element vonainbist. Idiomatisch für „gibt es irgendeine Überschneidung zwischen diesen zwei Mengen?"Collections.frequency(coll, target)zählt Vorkommen — viel klarer alscoll.stream().filter(x -> x.equals(target)).count().Collections.nCopies(n, x)ist manchmal genau das Richtige, z. B.result.addAll(Collections.nCopies(rows, "pad")). Die zurückgegebene Liste ist unveränderlich, belegt aber unabhängig vonnnur O(1) Speicher — sie ist eine virtuelle Liste, kein Backing-Array.Collections.reverse(list)ist in-place und stabil. Nicht selbst mit einerfor-Schleife nachbauen.Collections.addAll(coll, "a", "b", "c")ist kürzer und schneller alscoll.addAll(List.of("a", "b", "c")), weil die Zwischenliste entfällt.
Was Collections nicht ist
- Kein Stream-Ersatz. Für filter/map/reduce sollte man Streams verwenden.
Collectionsbefasst sich mit Mutation und direkten Abfragen, nicht mit deklarativen Pipelines. - Nicht der Ort für
List.of/Set.of/Map.of. Das sind Fabriken auf den Schnittstellen, die in Java 9 hinzugefügt wurden. Sie liegen nebenCollections.unmodifiableList, gehören aber nicht zu dieser Klasse. - Nicht der Ort für Stream-Collectors. Das ist
java.util.stream.Collectors. Anderes Paket, andere Rolle.
Ein vollständiges Beispiel: der Werkzeugkasten in einem Programm
Das folgende Programm wendet ein Dutzend Collections-Methoden auf eine einzelne Liste und eine einzelne Map an, um die API greifbar zu machen: sort, reverse, shuffle, rotate, swap, binarySearch, min/max, frequency, disjoint, fill, replaceAll und die nicht-veränderliche Sicht.
Was man aus der Ausführung mitnehmen kann:
- Jede Methode entweder mutiert in-place (
sort,reverse,shuffle,rotate,swap,fill,replaceAll) oder gibt einen primitiven Wert zurück (min,max,frequency,disjoint,binarySearch). Nichts im Werkzeugkasten gibt eine „neue" sortierte Liste zurück —Collections.sortmodifiziert die übergebene Liste. binarySearchhat den Index von"delta"zurückgegeben und einen negativen Wert für"zeta". Die Konvention-result - 1ergibt den Einfügepunkt, der die Liste sortiert halten würde.replaceAllhat einen String überall ersetzt, wo er vorkam;fillhat jeden Slot überschrieben. Beide arbeiten auf derselben Liste — praktisch, wenn man Speicher wiederverwenden möchte.Collections.unmodifiableList(backing)hat eine schreibgeschützte Sicht zurückgegeben. Die Sicht hat beiaddeine Ausnahme geworfen, aber das Mutieren der Backing-Liste funktionierte weiterhin, und die Änderung war durch die Sicht sichtbar. Die Sicht ist keine Kopie.
Was kommt als Nächstes
Der Werkzeugkasten ist nun auf Index-Ebene im Kopf. Zwei Operationen verdienen einen näheren Blick, weil ihre Details wichtig sind: Sortieren von Java-Collections (wann Collections.sort vs. List.sort vs. stream().sorted(), stabile Reihenfolge, Comparator-Builder, primitive Spezialisierungen) und Suchen in Java-Collections (contains, indexOf, binarySearch und streambasierte Suche). Das nächste Kapitel behandelt das Sortieren.