»Einen Rat befolgen heißt, die Verantwortung verschieben.« – Johannes Urzidil (1896–1970)
9 Besondere Klassen der Java SE
9.1 Vergleichen von Objekten 

Sollen Objekte verglichen werden, muss es eine Ordnung dieser Typen geben. Wie sollte das System sonst selbstständig entscheiden können, ob eine Person zum Beispiel kleiner als eine andere Person ist? Weil die eine Person 1,50 Meter groß ist, die andere aber 1,80 Meter, oder weil die eine Person eine Million Euro auf dem Konto hat und die andere nur fünf Euro? [Im 10. Jahrhundert lebte der Großwesir Abdul Kassem Ismael, der immer seine gesamte Bibliothek mit 117.000 Bänden mitführte. Die trainierten 400 Kamele transportierten die Werke in alphabetischer Reihenfolge. ] Diese Fragen sind wichtig, wenn wir zum Beispiel eine Liste sortieren wollen.
9.1.1 Natürlich geordnet oder nicht? 

In Java gibt es zwei unterschiedliche Schnittstellen (in zwei unterschiedlichen Paketen) zur Bestimmung der Ordnung:
- Comparable: Implementiert eine Klasse Comparable, so können sich die Objekte selbst mit anderen Objekten vergleichen. Da die Klassen im Allgemeinen nur ein Sortierkriterium implementieren, wird hierüber eine so genannte natürliche Ordnung (engl. natural ordering) realisiert.
- Comparator: Eine implementierende Klasse, die sich Comparator nennt, nimmt zwei Objekte an und vergleicht sie. Ein Comparator für Räume könnte zum Beispiel nach der Anzahl der Personen oder auch nach der Größe in Quadratmetern vergleichen; die Implementierung von Comparable wäre nicht sinnvoll, weil hier nur ein Kriterium natürlich umgesetzt werden kann, ein Raum aber nicht die Ordnung hat.
Zusammenfassend lässt sich sagen: Während Comparable üblicherweise nur ein Sortierkriterium umsetzt, kann es viele Extraklassen vom Typ Comparator geben, die jeweils unterschiedliche Ordnungen definieren.
Comparable und Comparator in der Java-API
Eine Implementierung von Comparable findet sich genau dort, wo eine natürliche Ordnung naheliegt, etwa bei:
- String
- BigDecimal, BigInteger, Byte, Character, Double, Float, Integer, Long, Short
- Date
- File, URI
- Enum
- TimeUnit
Von Comparator finden wir in der API-Dokumentation nur java.text.Collator vermerkt.
9.1.2 Die Schnittstelle Comparable 

Die Schnittstelle Comparable kommt aus dem java.lang-Paket und deklariert eine Methode:
|
interface java.lang.Comparable<T> |
- int compareTo( T o ) Vergleicht sich mit einem anderen.
|
Hinweis Wichtig ist neben einer Implementierung von compareTo() auch die passende Realisierung in equals(). Sie ist erst dann konsistent, wenn e1.compareTo(e2) == 0 das gleiche Ergebnis wie e1.equals(e2) liefert, wobei e1 und e2 den gleichen Typ besitzen. Ein Verstoß gegen diese Regel kann bei sortierten Mengen schnell Probleme bereiten; ein Beispiel nennt die API-Dokumentation. Auch sollte die hashCode()-Methode korrekt realisiert sein, denn sind Objekte gleich, müssen auch die Hashcodes gleich sein. Und die Gleichheit bestimmen eben equals()/compareTo(). e.compareTo(null) sollte eine NullPointerException auslösen, auch wenn e.equals(null) die Rückgabe false liefert. |
9.1.3 Die Schnittstelle Comparator 

Die Schnittstelle Comparator kommt aus dem Paket java.util und deklariert:
|
interface java.util.Comparator<T> |
- int compare( T o1, T o2 ) Vergleicht zwei Argumente auf ihre Ordnung.
- boolean equals( Object obj ) Testet, ob Comparator-Objekte gleich sind. Das testet keine Gleichheit von Objekten! Die Methode muss nicht zwingend implementiert werden, da ja schon Object eine Implementierung bereitstellt. Sie steht hier nur, damit eine API-Dokumentation dieses Missverständnis erklärt.
9.1.4 Rückgabewerte kodieren die Ordnung 

Der Rückgabewert von compare() beim Comparator beziehungsweise compareTo() bei Comparable ist <0, =0 oder >0 und bestimmt so die Ordnung der Objekte. Nehmen wir zwei Objekte o1 und o2 an, deren Klassen Comparable implementieren. Dann gilt folgende Übereinkunft:
- o1.compareTo( o2 ) < 0 o1 < o2
- o1.compareTo( o2 ) == 0 o1 == o2
- o1.compareTo( o2 ) > 0 o1 > o2
Ein externer Comparator (symbolisch comp genannt) verhält sich ähnlich:
- comp.compare( o1, o2 ) < 0 o1 < o2
- comp.compare( o1, o2 ) == 0 o1 == o2
- comp.compare( o1, o2 ) > 0 o1 > o2
|
Tipp Sollen Objekte mit einem Comparator verglichen werden, aber null-Werte vorher aussortiert werden, so wird seit Java 7 die statische Methode int compare(T a, T b, Comparator<? super T> c) aus der Klasse Objects nützlich. Die Methode liefert 0, wenn a und b beide entweder null sind oder der Comparator die Objekte a und b für gleich erklärt. Sind a und b beide ungleich null, so ist die Rückgabe c.compare(a, b). Ist nur a oder b gleich null, so hängt es vom Comparator und der Reihenfolge der Parameter ab. |
Den größten Raum einer Sammlung finden
Wir wollen Räume ihrer Größe nach sortieren und müssen dafür einen Comparator schreiben (dass Räume Comparable sind, ist nicht angebracht, da es keine natürliche Ordnung für Räume gibt). Daher soll ein externes Comparator-Objekt entscheiden, welches Raum-Objekt nach der Anzahl seiner Quadratmeter größer ist.
Der Raum enthält für das kleine Demoprogramm nur einen parametrisierten Konstruktor und merkt sich dort seine Quadratmeter:
Listing 9.1 com/tutego/insel/util/RoomComparatorDemo.java, Room
class Room
{
int sm;
Room( int sm )
{
this.sm = sm;
}
}Der spezielle Raum-Comparator ist das eigentlich Interessante:
Listing 9.2 com/tutego/insel/util/RoomComparatorDemo.java, RoomComparator
class RoomComparator implements Comparator<Room> { @Override public int compare( Room room1, Room room2 ) { return room1.sm – room2.sm; } }
Er bildet die Differenz der Raumgrößen, was eine einfache Möglichkeit darstellt, eine Rückgabe <0, =0 oder >0 zu bekommen. Bei Fließkommazahlen funktioniert das nicht – denn (int)(0.1 – 0.0), (int)(0.0 – 0.1) und (int)(0.0 – 0.0) ergeben alle 0, wären also gleich – bei Ganzzahlen ist der Vergleich aber in Ordnung. Ab Java 7 lässt sich Integer.compareTo(room1.sm, room2.sm) einsetzen.
Mit dem Comparator-Objekt lässt sich eine Raumliste sortieren:
Listing 9.3 com/tutego/insel/util/RoomComparatorDemo.java, RoomComparatorDemo main()
List<Room> list = Arrays.asList(new Room(100), new Room(1123), new Room(123) ); Collections.sort( list, new RoomComparator() ); System.out.println( list.get(0).sm ); // 100
|
Hinweis Ist ein Comparator mit einer Datenstruktur – wie dem TreeSet oder der TreeMap – verbunden, muss die Comparator-Klasse Serializable (siehe Kapitel 17, »Datenströme«) implementieren, wenn auch die Datenstruktur serialisiert werden soll. |






Jetzt bestellen





