Advanced Sorting
Kurz: Sortieren nach eigenen, komplexeren Kriterien statt der natürlichen Standardordnung — z. B. nach mehreren Feldern gleichzeitig oder in umgekehrter Reihenfolge.
Genauer: Comparator bietet dafür Verkettungsmethoden wie thenComparing() (sekundäres Sortierkriterium bei Gleichstand) und reversed() (Reihenfolge umkehren). Damit lassen sich z. B. Objekte zuerst nach Nachname, bei Gleichstand nach Vorname sortieren, ohne eine eigene komplexe Vergleichslogik von Hand zu schreiben.
liste.sort(Comparator.comparing(Person::getNachname).thenComparing(Person::getVorname));Im Detail
Die Standard-sort()-Methode auf einer List nimmt entweder gar kein Argument (dann muss die Klasse Comparable implementieren und ihre “natürliche” Ordnung über compareTo() selbst festlegen) oder einen Comparator als Argument, der die Sortierlogik von außen vorgibt, ohne die sortierte Klasse selbst anfassen zu müssen — das ist der übliche Weg, wenn man nach unterschiedlichen Kriterien sortieren will, ohne die Klasse jedes Mal zu ändern.
record Person(String nachname, String vorname, int alter) {}
List<Person> personen = new ArrayList<>(List.of(
new Person("Müller", "Anna", 30),
new Person("Müller", "Ben", 25),
new Person("Schmidt", "Zoe", 40)
));
// Nach Nachname, bei Gleichstand nach Vorname, absteigend nach Alter
personen.sort(
Comparator.comparing(Person::nachname)
.thenComparing(Person::vorname)
.thenComparing(Comparator.comparingInt(Person::alter).reversed())
);Wichtige Bausteine: Comparator.comparing(Getter) für ein Feld, .thenComparing(...) verkettet weitere Kriterien für Gleichstände, .reversed() kehrt die Richtung um, und Comparator.comparingInt/-Long/-Double vermeidet unnötiges Autoboxing bei primitiven Feldern (ein Comparator.comparing(Person::alter) würde jeden int-Wert erst in ein Integer-Objekt verpacken, um ihn generisch vergleichen zu können — bei sehr großen Listen ein messbarer Performance-Unterschied). Für Collections mit natürlicher Ordnung reicht oft einfach Collections.sort(liste), sobald die Elemente Comparable implementieren.
Comparable vs. Comparator
Comparable<T> (mit der Methode compareTo()) wird von der zu sortierenden Klasse selbst implementiert und legt EINE feste “natürliche” Ordnung fest — z. B. sortiert String natürlich alphabetisch, Integer natürlich numerisch aufsteigend. Comparator<T> dagegen kommt von AUSSEN und kann beliebig viele unterschiedliche Sortierlogiken für dieselbe Klasse bereitstellen, ohne die Klasse selbst zu verändern:
class Produkt implements Comparable<Produkt> {
String name;
double preis;
@Override
public int compareTo(Produkt andere) {
return Double.compare(this.preis, andere.preis); // "natürliche" Ordnung: nach Preis
}
}
// Trotzdem jederzeit anders sortierbar, ohne Produkt zu ändern:
produkte.sort(Comparator.comparing(p -> p.name)); // alphabetisch
produkte.sort(Comparator.comparingDouble((Produkt p) -> p.preis).reversed()); // teuerste zuerstStabilität und typische Fallstricke
Javas Sortieralgorithmen (Collections.sort(), Arrays.sort() für Objekte) sind garantiert stabil — Elemente, die laut Vergleichslogik gleich sind, behalten ihre ursprüngliche relative Reihenfolge. Das ist bei mehrstufigem Sortieren wichtig: sortiert man erst nach Vorname und danach (stabil) nach Nachname, landen bei gleichem Nachname die Einträge trotzdem alphabetisch nach Vorname sortiert. Ein häufiger Anfängerfehler ist ein inkonsistenter Comparator, der z. B. bei a.compareTo(b) und b.compareTo(a) widersprüchliche Vorzeichen liefert (etwa durch fehlerhafte Subtraktion bei int-Werten, die bei sehr großen/kleinen Zahlen überlaufen kann) — das führt zu IllegalArgumentException: Comparison method violates its general contract! zur Laufzeit, oft erst bei größeren Listen, weil kleinere Listen mit anderen internen Sortier-Algorithmen behandelt werden.
Siehe auch: Sorting, Lambda, Generics, Collections