Part 10

Das Comparable-Interface

Im vorherigen Abschnitt haben wir Schnittstellen (Interfaces) allgemein betrachtet – lassen Sie uns nun mit einer der vordefinierten Schnittstellen von Java vertraut machen. Das Comparable-Interface definiert die Methode compareTo, die zum Vergleichen von Objekten verwendet wird. Wenn eine Klasse das Comparable-Interface implementiert, können Objekte, die aus dieser Klasse erstellt wurden, mit den Sortieralgorithmen von Java sortiert werden.

Die compareTo-Methode, die vom Comparable-Interface gefordert wird, erhält als Parameter das Objekt, mit dem das „this“-Objekt verglichen wird. Wenn das „this“-Objekt in der Sortierreihenfolge vor dem als Parameter übergebenen Objekt kommt, sollte die Methode eine negative Zahl zurückgeben. Wenn das „this“-Objekt dagegen nach dem als Parameter übergebenen Objekt kommt, sollte die Methode eine positive Zahl zurückgeben. Andernfalls wird 0 zurückgegeben. Die durch die compareTo-Methode erzielte Sortierung wird als natürliche Ordnung bezeichnet.

Betrachten wir dies anhand einer Klasse Member, die ein Kind oder einen Jugendlichen darstellt, der einem Club angehört. Jedes Clubmitglied hat einen Namen und eine Körpergröße. Die Clubmitglieder sollten in der Reihenfolge ihrer Körpergröße essen gehen, daher sollte die Member-Klasse das Comparable-Interface implementieren. Das Comparable-Interface verwendet als Typ-Parameter die Klasse, die Gegenstand des Vergleichs ist. Wir werden die gleiche Klasse Member als Typ-Parameter verwenden.

public class Member implements Comparable<Member> {
    private String name;
    private int height;

    public Member(String name, int height) {
        this.name = name;
        this.height = height;
    }

    public String getName() {
        return this.name;
    }

    public int getHeight() {
        return this.height;
    }

    @Override
    public String toString() {
        return this.getName() + " (" + this.getHeight() + ")";
    }

    @Override
    public int compareTo(Member member) {
        if (this.height == member.getHeight()) {
            return 0;
        } else if (this.height > member.getHeight()) {
            return 1;
        } else {
            return -1;
        }
    }
}

Die vom Interface geforderte compareTo-Methode gibt eine ganze Zahl zurück, die uns über die Reihenfolge des Vergleichs informiert.

Da es ausreicht, eine negative Zahl von compareTo() zurückzugeben, wenn das this-Objekt kleiner ist als das Parameterobjekt, und null, wenn die Längen gleich sind, kann die oben beschriebene compareTo-Methode auch wie folgt implementiert werden.

@Override
public int compareTo(Member member) {
    return this.height - member.getHeight();
}

Da die Member-Klasse das Comparable-Interface implementiert, ist es möglich, die Liste mit der sorted-Methode zu sortieren. Tatsächlich können Objekte jeder Klasse, die das Comparable-Interface implementiert, mit der sorted-Methode sortiert werden. Beachten Sie jedoch, dass ein Stream die ursprüngliche Liste nicht sortiert – nur die Elemente im Stream werden sortiert.

Wenn ein Programmierer die ursprüngliche Liste organisieren möchte, sollte die sort-Methode der Collections-Klasse verwendet werden. Dies setzt natürlich voraus, dass die Objekte auf der Liste das Comparable-Interface implementieren.

Das Sortieren von Clubmitgliedern ist jetzt unkompliziert.

List<Member> member = new ArrayList<>();
member.add(new Member("mikael", 182));
member.add(new Member("matti", 187));
member.add(new Member("ada", 184));

member.stream().forEach(m -> System.out.println(m));
System.out.println();
// sorting the stream that is to be printed using the sorted method
member.stream().sorted().forEach(m -> System.out.println(m));
member.stream().forEach(m -> System.out.println(m));
// sorting a list with the sort-method of the Collections class
Collections.sort(member);
member.stream().forEach(m -> System.out.println(m));
Beispielausgabe

mikael (182) matti (187) ada (184)

mikael (182) ada (184) matti (187)

mikael (182) matti (187) ada (184)

mikael (182) ada (184) matti (187)

Quiz

Loading...

Loading
Loading

Sortiermethode als Lambda-Ausdruck

Nehmen wir an, wir haben die folgende Person-Klasse zur Verfügung.

public class Person {

    private String name;
    private int birthYear;

    public Person(String name, int birthYear) {
        this.name = name;
        this.birthYear = birthYear;
    }

    public String getName() {
        return name;
    }

    public int getBirthYear() {
        return birthYear;
    }
}

Und Personenobjekte in einer Liste.

ArrayList<Person> persons = new ArrayList<>();
persons.add(new Person("Ada Lovelace", 1815));
persons.add(new Person("Irma Wyman", 1928));
persons.add(new Person("Grace Hopper", 1906));
persons.add(new Person("Mary Coombs", 1929));

Wir möchten die Liste sortieren, ohne dass das Comparable-Interface implementiert werden muss.

Sowohl die sort-Methode der Collections-Klasse als auch die sorted-Methode des Streams akzeptieren einen Lambda-Ausdruck als Parameter, der die Sortierkriterien definiert. Genauer gesagt, können beide Methoden mit einem Objekt versehen werden, das das Comparator-Interface implementiert, welches die gewünschte Reihenfolge definiert – der Lambda-Ausdruck

wird verwendet, um dieses Objekt zu erstellen.

ArrayList<Person> persons = new ArrayList<>();
persons.add(new Person("Ada Lovelace", 1815));
persons.add(new Person("Irma Wyman", 1928));
persons.add(new Person("Grace Hopper", 1906));
persons.add(new Person("Mary Coombs", 1929));

persons.stream().sorted((p1, p2) -> {
    return p1.getBirthYear() - p2.getBirthYear();
}).forEach(p -> System.out.println(p.getName()));

System.out.println();

persons.stream().forEach(p -> System.out.println(p.getName()));

System.out.println();

Collections.sort(persons, (p1, p2) -> p1.getBirthYear() - p2.getBirthYear());

persons.stream().forEach(p -> System.out.println(p.getName()));
Beispielausgabe

Ada Lovelace Grace Hopper Irma Wyman Mary Coombs

Ada Lovelace Irma Wyman Grace Hopper Mary Coombs

Ada Lovelace Grace Hopper Irma Wyman Mary Coombs

Beim Vergleich von Zeichenfolgen können wir die compareTo-Methode der String-Klasse verwenden. Die Methode gibt eine ganze Zahl zurück, die die Reihenfolge der Zeichenfolgen beschreibt, die ihr als Parameter übergeben wird, und der Zeichenfolge, die sie aufruft.


ArrayList<Person> persons = new ArrayList<>();
persons.add(new Person("Ada Lovelace", 1815));
persons.add(new Person("Irma Wyman", 1928));
persons.add(new Person("Grace Hopper", 1906));
persons.add(new Person("Mary Coombs", 1929));

persons.stream().sorted((p1, p2) -> {
    return p1.getName().compareTo(p2.getName());
}).forEach(p -> System.out.println(p.getName()));
Beispielausgabe

Ada Lovelace Grace Hopper Irma Wyman Mary Coombs

Loading

Sortieren nach mehreren Kriterien

Manchmal möchten wir Elemente nach mehreren Kriterien sortieren. Sehen wir uns ein Beispiel an, bei dem Filme nach ihrem Namen und Erscheinungsjahr in der Reihenfolge aufgelistet werden. Wir nutzen hier die Comparator-Klasse von Java, die uns die erforderliche Funktionalität für das Sortieren bietet. Nehmen wir an, wir haben die Klasse Film zur Verfügung.

public class Film {
    private String name;
    private int releaseYear;

    public Film(String name, int releaseYear) {
        this.name = name;
        this.releaseYear = releaseYear;
    }

    public String getName() {
        return this.name;
    }

    public int getReleaseYear() {
        return this.releaseYear;
    }

    public String toString() {
        return this.name + " (" + this.releaseYear + ")";
    }
}

Die Comparator-Klasse bietet zwei wesentliche Methoden zum Sortieren: comparing und thenComparing. Der comparing-Methode wird der Wert übergeben, der zuerst verglichen werden soll, und der thenComparing-Methode der nächste zu vergleichende Wert. Die thenComparing-Methode kann mehrmals durch Verkettung von Methoden verwendet werden, was es ermöglicht, nahezu unbegrenzt viele Werte für den Vergleich zu verwenden.

Wenn wir Objekte sortieren, werden den Methoden comparing und thenComparing eine Referenz auf den Objekttyp übergeben – die Methode wird der Reihe nach aufgerufen und die von der Methode zurückgegebenen Werte werden verglichen. Die Methodenreferenz wird als Class::method angegeben. Im unten stehenden Beispiel geben wir Filme nach Jahr und Titel in der Reihenfolge aus.

List<Film> films = new ArrayList<>();
films.add(new Film("A", 2000));
films.add(new Film("B", 1999));
films.add(new Film("C", 2001));
films.add(new Film("D", 2000));

for (Film e: films) {
    System.out.println(e);
}

Comparator<Film> comparator = Comparator
              .comparing(Film::getReleaseYear)
              .thenComparing(Film::getName);

Collections.sort(films, comparator);

for (Film e: films) {
    System.out.println(e);
}
Beispielausgabe

A (2000) B (1999) C (2001) D (2000)

B (1999) A (2000) D (2000) C (2001)

Loading
Sie haben das Ende dieses Abschnitts erreicht! Weiter zum nächsten Abschnitt: