

Dieser Artikel erklärt:
- Wie vergleicht man zwei Objekte in Java?
- Was ist ein
Comparator
und wofür benötigt man ihn? - Welche Möglichkeiten gibt es, um einen
Comparator
zu erzeugen? - Wie erzeugt man einen
Comparator
mit Java 8? - Was ist der Unterschied zwischen
Comparator
undComparable
?
Doch vorab die Frage: Warum möchte man überhaupt Java-Objekte miteinander vergleichen?
Das wichtigste Einsatzgebiet für den Vergleich zweier Objekte ist sicherlich das Sortieren von Objekt-Listen oder -Arrays. Um Objekte zu sortieren, muss das Programm sie vergleichen und herausfinden, ob ein Objekt kleiner, größer oder gleich einem anderen ist.
Den Quellcode zum Artikel findest du im zugehörigen GitHub-Repository.
Wie vergleicht man zwei Objekte in Java?
Java-Primitive (int
, long
, double
, etc.) vergleicht man mit den Operatoren <
, <=
, ==
, =>
, >
.
Bei Objekten funktioniert das nicht.
Dort verwendet man stattdessen entweder eine compare
- oder eine compareTo
-Methode. Beide Methoden erkläre ich am Beispiel von Strings und der selbst geschriebenen Klasse "Student".
Wie vergleicht man zwei Strings in Java?
Nehmen wir an, wir haben die folgenden zwei Strings:
String s1 = "Happy";
String s2 = "Coders";
Code-Sprache: Java (java)
Wir wollen nun herausfinden, ob s1
kleiner, größer oder gleich s2
ist. Oder anders gesagt: ob – nach alphabetischer Sortierung* – s1
vor oder nach s2
käme.
Das tun wir wie folgt:
int result = s1.compareTo(s2);
Code-Sprache: Java (java)
Die Variable result
enthält nun:
- einen Wert kleiner als 0, wenn
s1
nach alphabetischer Sortierung vors2
kommt - 0, wenn
s1
unds2
gleich sind (d. h., dasss1.equals(s2) true
ergibt) - einen Wert größer als 0, wenn
s1
nach alphabetischer Sortierung nachs2
kommt
Im Beispiel oben wäre result
größer als 0, da "Happy" hinter "Coders" einsortiert werden würde.
(* Alphabetisch bedeutet im Fall von String.compareTo()
: entsprechend der Unicode-Werte der Buchstaben, d. h. beispielsweise, dass ein Großbuchstabe immer vor einem Kleinbuchstaben kommt, und dass die deutschen Umlaute erst nach allen regulären Groß- und Kleinbuchstaben kommen)
Sortieren von Strings in Java
Die compareTo()
-Methode wird hinter den Kulissen verwendet, um ein Array oder eine Liste von Objekten zu sortieren, beispielsweise wie folgt:
public class NameSortExample {
public static void main(String[] args) {
String[] names = {"Mary", "James", "Patricia", "John", "Jennifer", "Robert"};
Arrays.sort(names);
System.out.println(Arrays.toString(names));
}
}
Code-Sprache: Java (java)
Das Programm gibt die Namen in alphabetischer Reihenfolge aus:
[James, Jennifer, John, Mary, Patricia, Robert]
Code-Sprache: Klartext (plaintext)
(Welche Möglichkeiten es noch gibt, um in Java Objekte oder Primitive, wie int
, long
, double
zu sortieren, erfährst du im Tutorial "Sortieren in Java".)
Was aber, wenn wir Strings gar nicht alphabetisch sortieren wollen, sondern beispielsweise nach ihrer Länge? Dazu brauchen wir einen sogenannten Comparator
. Bevor wir dazu kommen, erkläre ich erst einmal das Interface Comparable
, das wir soeben verwendet haben.
Das Java Comparable Interface
Die String.compareTo()
-Methode, die wir oben angewendet haben, kommt aus dem java.lang.Comparable
-Interface, welches von der String
-Klasse implementiert wird.
Das Comparable
-Interface definiert nur diese eine Methode. Es wird von allen Klassen implementiert, deren Objekte vergleichbar (englisch: "comparable") sein sollen. Neben String
sind das beispielsweise Integer
, BigInteger
, Long
, Date
, LocalDateTime
, und viele mehr.
Die Reihenfolge, die sich aus der compareTo()
-Methode ergibt, nennt man "natürliche Ordnung": Strings werden alphabetisch sortiert, Datums- und Zeitwerte in ihrer zeitlichen Abfolge.
Wie du eine selbst entwickelte Klasse vergleichbar (und damit sortierbar) machst, liest du im folgenden Abschnitt.
Java Comparable Beispiel
Um selbstentwickelte Klassen vergleichbar und damit sortierbar zu machen, müssen sie das Interface Comparable
und dessen compareTo()
-Methode implementieren.
Das folgende Beispiel zeigt, wie man dies für eine Student
-Klasse macht, die standardmäßig nach Matrikelnummer (ID) sortiert werden sollen:
public class Student implements Comparable<Student> {
private int id;
private String firstName;
private String lastName;
// ... constructor ...
// ... getters and setters ...
// ... toString() method ...
@Override
public int compareTo(Student o) {
if (this.id < o.id) {
return -1;
} else if (this.id == o.id) {
return 0;
} else {
return 1;
}
}
}
Code-Sprache: Java (java)
Mit dem ternären Operator kannst du die compareTo()
-Methode auch als Einzeiler notieren:
@Override
public int compareTo(Student o) {
return this.id < o.id ? -1 : (this.id == o.id ? 0 : 1);
}
Code-Sprache: Java (java)
Noch eleganter ist es allerdings für den Vergleich der zwei int
-Werte die statische Methode Integer.compare()
aufzurufen:
@Override
public int compareTo(Student o) {
return Integer.compare(this.id, o.id);
}
Code-Sprache: Java (java)
Keine Sorge, die JVM inlined diesen Methodenaufruf, so dass es zu keinerlei Performance-Einbußen kommt.
Achtung Falle: ints subtrahieren
Mitunter sieht man in einer compareTo()
-Methode folgendes:return this.id - o.id;
Das sollte man auf keinen Fall so schreiben, denn es funktioniert nicht, wenn beispielsweise this.id
-2.000.000.000 ist und o.id
1.000.000.000. Da in diesem Fall this.id
kleiner ist, sollte die compareTo()
-Methode eine negative Zahl zurückliefern. Das tut sie aber nicht, da es bei der Subtraktion zu einem arithmetischen Unterlauf kommt – das Ergebnis der Subtraktion ist 1.294.967.296 – eine positive Zahl!
Und hier ein Beispiel, in dem drei Studenten erzeugt, in eine Liste geschrieben und anschließend sortiert werden:
public class StudentSortExample {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student(47271, "Kerrie", "Adkins"));
students.add(new Student(99319, "Aarron", "Wicks"));
students.add(new Student(11056, "Kaya", "Molina"));
Collections.sort(students);
System.out.println("students = " + students);
}
}
Code-Sprache: Java (java)
Wie erwartet, werden die Studenten nach Matrikelnummer sortiert ausgegeben (die Zeilenumbrüche habe ich der Übersicht halber manuell eingefügt):
students = [Student{id=11056, firstName='Kaya', lastName='Molina'},
Student{id=47271, firstName='Kerrie', lastName='Adkins'},
Student{id=99319, firstName='Aarron', lastName='Wicks'}]
Code-Sprache: Klartext (plaintext)
Kommen wir zurück zum String, den wir im nächsten Schritt nicht alphabetisch, sondern nach Länge sortieren wollen.
Das Java Comparator Interface
Um zwei Objekte nach einer anderen als ihrer natürlicher Ordnung zu sortieren (oder um Objekte von Klassen zu sortieren, die Comparable
nicht implementieren), verwenden wir das java.util.Comparator
-Interface.
Dieses definiert die Methode compare(T o1, T o2)
, mit der die zwei übergebenen Objekte verglichen werden. Die Methode hat analog zur compareTo()
-Methode folgende Rückgabewerte:
- einen Wert kleiner als 0, wenn
o1
kleiner ist also2
- 0, wenn
o1
undo2
gleich sind (d. h., dasso1.equals(o2) true
ergibt) - einen Wert größer als 0, wenn
o1
größer ist also2
Java Comparator Beispiel: Strings nach Länge sortieren
Einen Comparator, der Strings nach ihrer Länge vergleicht, würden wir wie folgt implementieren:
public class StringLengthComparator implements Comparator<String> {
@Override
public int compare(String o1, String o2) {
if (o1.length() < o2.length()) {
return -1;
} else if (o1.length() == o2.length()) {
return 0;
} else {
return 1;
}
}
}
Code-Sprache: Java (java)
Auch hier können wir den Code mit Hilfe des ternären Operators wieder auf eine Zeile komprimieren:
@Override
public int compare(String o1, String o2) {
return o1.length() < o2.length() ? -1 : (o1.length() == o2.length() ? 0 : 1);
}
Code-Sprache: Java (java)
Eingesetzt wird der StringLengthComparator
beispielsweise wie folgt:
public class NameSortByLengthExample {
public static void main(String[] args) {
String[] names = {"Mary", "James", "Patricia", "John", "Jennifer", "Robert"};
Arrays.sort(names, new StringLengthComparator());
System.out.println(Arrays.toString(names));
}
}
Code-Sprache: Java (java)
Die Namen werden nun nicht mehr alphabetisch sortiert, sondern aufsteigend nach Länge:
[Mary, John, James, Robert, Patricia, Jennifer]
Code-Sprache: Klartext (plaintext)
Wie erstellt man einen Comparator?
Bis Java 7 konnte man einen Comparator ausschließlich – wie im Beispiel oben gezeigt – durch Implementierung des Comparator
-Interfaces erstellen.
Seit Java 8 kann man einen Comparator auch als Lambda notieren oder ihn – ziemlich komfortabel, wie du gleich sehen wirst – mit Hilfe der Methoden Comparator.comparing()
, thenComparing()
und reversed()
erzeugen.
Comparator als öffentliche Klasse
Am Beispiel StringLengthComparator
haben wir bereits die erste Variante kennengelernt: Wir schreiben eine öffentliche Klasse und übergeben der Sortiermethode eine Instanz davon:
Arrays.sort(names, new StringLengthComparator());
Code-Sprache: Java (java)
Wenn wir an mehreren Stellen nach Stringlänge sortieren wollen, können wir auch eine Konstante extrahieren:
private static final StringLengthComparator STRING_LENGTH_COMPARATOR =
new StringLengthComparator();
Code-Sprache: Java (java)
Alternativ könnten wir ein Singleton definieren:
public class StringLengthComparator implements Comparator<String> {
public static final StringLengthComparator INSTANCE = new StringLengthComparator();
private StringLengthComparator() {}
@Override
public int compare(String o1, String o2) {
return Integer.compare(o1.length(), o2.length());
}
}
Code-Sprache: Java (java)
Eine öffentliche Klasse gibt uns auch die Möglichkeit das Sortierverhalten über Konstruktor-Parameter zu steuern. Z. B. könnten wir die Sortierreihenfolge konfigurierbar machen:
public class StringLengthComparator implements Comparator<String> {
public static final StringLengthComparator ASC = new StringLengthComparator(true);
public static final StringLengthComparator DESC = new StringLengthComparator(false);
private final boolean ascending;
private StringLengthComparator(boolean ascending) {
this.ascending = ascending;
}
@Override
public int compare(String o1, String o2) {
int result = Integer.compare(o1.length(), o2.length());
return ascending ? result : -result;
}
}
Code-Sprache: Java (java)
Diesen Comparator würden wir beispielsweise wie folgt einsetzen, um unsere Namensliste absteigend zu sortieren:
Arrays.sort(names, StringLengthComparator.DESC);
Code-Sprache: Java (java)
Eine öffentliche Klasse gibt uns also größtmögliche Freiheit und Flexibilität bei der Definition unseres Comparators.
Comparator als anonyme Klasse
Benötigen wir einen Comparator nur an einer einzigen Stelle, können wir ihn auch als anonyme Klasse definieren.
Mit dem folgenden Code beispielsweise sortieren wir unsere Studenten nach Nachnamen:
students.sort(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getLastName().compareTo(o2.getLastName());
}
});
Code-Sprache: Java (java)
Da es Nachnamen gibt, die häufiger vorkommen, sollten wir vielleicht besser nach Nachnamen und nach Vornamen sortieren. Dies tun wir, in dem wir zunächst prüfen, ob die Nachnamen gleich sind. Wenn das der Fall ist, vergleichen wir auch die Vornamen:
students.sort(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int result = o1.getLastName().compareTo(o2.getLastName());
if (result == 0) {
result = o1.getFirstName().compareTo(o2.getFirstName());
}
return result;
}
});
Code-Sprache: Java (java)
In beiden Fällen wird uns eine moderne IDE wie IntelliJ darauf hinweisen, dass das ab Java 8 auch eleganter geht (und uns optimalerweise auch gleich anbieten den Code zu refactoren):

Was dabei herauskommt, erfährst du im nächsten Abschnitt.
Java Comparator als Lambda notieren
Ab Java 8 können wir statt der anonymen Klasse nämlich auch ein Lambda verwenden. Das Sortieren nach Nachname erfolgt dann wie folgt:
students.sort((o1, o2) -> o1.getLastName().compareTo(o2.getLastName()));
Code-Sprache: Java (java)
Auch das Sortieren nach Nach- und Vorname wird durch die Lambda-Notation kürzer:
students.sort((o1, o2) -> {
int result = o1.getLastName().compareTo(o2.getLastName());
if (result == 0) {
result = o1.getFirstName().compareTo(o2.getFirstName());
}
return result;
});
Code-Sprache: Java (java)
Richtig schön wird es mit der Methode, die ich im folgenden Abschnitt zeigen werde. Auch den Schritt dorthin bietet uns eine moderne IDE an:

Java 8: Comparator erzeugen mit Comparator.comparing()
Die eleganteste Methode zur Konstruktion eines Comparators, die ebenfalls seit Java 8 zur Verfügung steht, ist die Verwendung von Comparator.comparing()
, Comparator.thenComparing()
und Comparator.reversed()
– sowie deren Variationen für die primitiven Datentypen int
, long
und double
.
Um die Studenten nach Nachnamen zu sortieren, schreiben wir folgendes:
students.sort(Comparator.comparing(Student::getLastName));
Code-Sprache: Java (java)
Wir übergeben also lediglich eine Referenz auf die Methode, die das Feld zurückgibt, nach welchem sortiert werden soll.
Nach Nach- und Vornamen sortieren wir wie folgt:
students.sort(Comparator.comparing(Student::getLastName)
.thenComparing(Student::getFirstName));
Code-Sprache: Java (java)
Diese Schreibweise macht es uns sehr einfach auch den Fall abzudecken, dass zwei Studenten nicht nur den gleichen Nach- sondern auch den gleichen Vornamen haben. Um sie in diesem Fall zusätzlich nach ID zu sortieren, müssen wir lediglich ein thenComparingInt()
hinzufügen:
students.sort(Comparator.comparing(Student::getLastName)
.thenComparing(Student::getFirstName)
.thenComparingInt(Student::getId));
Code-Sprache: Java (java)
Comparator.comparing()
und die damit aufgebauten Comparator-Ketten machen den Code nicht nur kürzer, sondern auch deutlich aussagekräftiger.
Comparable vs Comparator – Zusammenfassung
Wir haben im Laufe des Artikels die Interfaces java.lang.Comparable
und java.util.Comparator
kennengelernt. Hier noch einmal der Unterschied in wenigen Sätzen zusammengefasst:
Durch die Implementierung der Comparable
.compareTo()
-Methode definieren wir die natürliche Ordnung von Objekten einer Klasse, d. h. diejenige Ordnung, nach der die Objekte der Klasse standardmäßig sortiert werden, also beispielsweise durch Arrays.sort(arrayOfObjects)
oder Collections.sort(listOfObjects)
.
Mit einem Comparator
können wir Objekte in einer anderen als ihrer natürlichen Ordnung sortieren. Und wir können Objekte sortieren, die das Comparable
-Interface nicht implementieren, also keine natürliche Ordnung haben.
Seit Java 8 lassen sich Comparatoren sehr elegant definieren, wie z. B. in students.sort(Comparator.comparing(Student::getLastName)
.
Wenn dir der Artikel gefallen hat, teile den Artikel über einen der Share-Buttons am Ende oder hinterlasse mir einen Kommentar.
Möchtest du informiert werden, wenn neue Artikel auf HappyCoders.eu veröffentlicht werden? Dann klicke hier, um dich für den HappyCoders.eu-Newsletter anzumelden.