Suche…


Syntax

  • public class MyClass implementiert Comparable <MyClass >
  • public class MyComparator implementiert Comparator <SomeOtherClass >
  • public int compareTo (Meine andere)
  • public int compare (SomeOtherClass o1, SomeOtherClass o2)

Bemerkungen

Wenn Sie eine compareTo(..) -Methode implementieren, die von einem double abhängig ist, gehen Sie nicht wie folgt vor:

public int comareTo(MyClass other) {
    return (int)(doubleField - other.doubleField); //THIS IS BAD
}

Die durch die (int) -Umgabe verursachte (int) führt dazu, dass die Methode manchmal falsch anstatt einer positiven oder negativen Zahl 0 zurückgibt, was zu Vergleichen und Sortieren von Fehlern führen kann.

Stattdessen ist die einfachste korrekte Implementierung die Verwendung von Double.compare als solchen:

public int comareTo(MyClass other) {
    return Double.compare(doubleField,other.doubleField); //THIS IS GOOD
} 

Eine nicht generische Version von Comparable<T> , einfach Comparable , existiert seit Java 1.2 . Abgesehen von der Anbindung an älteren Code ist es immer besser, die generische Version Comparable<T> zu implementieren, da kein Casting beim Vergleich erforderlich ist.


Es ist sehr Standard, dass eine Klasse mit sich selbst vergleichbar ist, wie in:

public class A implements Comparable<A>

Es ist zwar möglich, von diesem Paradigma abzubrechen, seien Sie jedoch vorsichtig.


Ein Comparator<T> kann weiterhin für Instanzen einer Klasse verwendet werden, wenn diese Klasse Comparable<T> implementiert. In diesem Fall wird die Logik des Comparator verwendet. Die von der Comparable Implementierung angegebene natürliche Reihenfolge wird ignoriert.

Sortieren einer Liste mit Comparable oder ein Komparator

Angenommen, wir arbeiten an einer Klasse, die eine Person mit ihrem Vor- und Nachnamen darstellt. Wir haben dafür eine grundlegende Klasse erstellt und richtige equals und hashCode Methoden implementiert.

public class Person {

    private final String lastName; //invariant - nonnull
    private final String firstName; //invariant - nonnull

    public Person(String firstName, String lastName){
        this.firstName = firstName != null ? firstName : "";
        this.lastName = lastName != null ? lastName : "";
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public String toString() {
        return lastName + ", " + firstName;
    }

    @Override
    public boolean equals(Object o) {
        if (! (o instanceof Person)) return false;
        Person p = (Person)o;
        return firstName.equals(p.firstName) && lastName.equals(p.lastName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(firstName, lastName);
    }
}

Nun möchten wir eine Liste von Person nach ihrem Namen sortieren, wie im folgenden Szenario:

public static void main(String[] args) {
    List<Person> people = Arrays.asList(new Person("John", "Doe"),
                                     new Person("Bob", "Dole"),
                                     new Person("Ronald", "McDonald"),
                                     new Person("Alice", "McDonald"),
                                     new Person("Jill", "Doe"));
    Collections.sort(people); //This currently won't work.
}

Leider werden die oben genannten Punkte derzeit nicht kompiliert. Collections.sort(..) eine Liste nur sortieren, wenn die Elemente in dieser Liste vergleichbar sind oder eine benutzerdefinierte Vergleichsmethode angegeben wird.

Wenn Sie gebeten werden, die folgende Liste zu sortieren: 1,3,5,4,2 , haben Sie kein Problem mit der Antwort 1,2,3,4,5 . Dies liegt daran, dass Integer (sowohl in Java als auch mathematisch) eine natürliche Reihenfolge haben , eine Standard-Standardvergleichsbasisordnung. Um unserer Person-Klasse eine natürliche Reihenfolge zu geben, implementieren wir compareTo(Person p): Comparable<Person> , was die Implementierung der Methode compareTo(Person p): erfordert compareTo(Person p):

public class Person implements Comparable<Person> {

    private final String lastName; //invariant - nonnull
    private final String firstName; //invariant - nonnull

    public Person(String firstName, String lastName) {
        this.firstName = firstName != null ? firstName : "";
        this.lastName = lastName != null ? lastName : "";
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public String toString() {
        return lastName + ", " + firstName;
    }

    @Override
    public boolean equals(Object o) {
        if (! (o instanceof Person)) return false;
        Person p = (Person)o;
        return firstName.equals(p.firstName) && lastName.equals(p.lastName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(firstName, lastName);
    }

    @Override
    public int compareTo(Person other) {
        // If this' lastName and other's lastName are not comparably equivalent,
        // Compare this to other by comparing their last names.
        // Otherwise, compare this to other by comparing their first names
        int lastNameCompare = lastName.compareTo(other.lastName);
        if (lastNameCompare != 0) {
            return lastNameCompare;
        } else {
            return firstName.compareTo(other.firstName);
        }
    }
}

Die angegebene Hauptmethode funktioniert nun korrekt

public static void main(String[] args) {
    List<Person> people = Arrays.asList(new Person("John", "Doe"),
                                     new Person("Bob", "Dole"),
                                     new Person("Ronald", "McDonald"),
                                     new Person("Alice", "McDonald"),
                                     new Person("Jill", "Doe"));
    Collections.sort(people); //Now functions correctly

    //people is now sorted by last name, then first name:
    // --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald
}

Wenn Sie jedoch die Klasse Person nicht ändern möchten oder können, können Sie einen benutzerdefinierten Comparator<T> bereitstellen, der den Vergleich von zwei Person übernimmt. Wenn Sie aufgefordert wurden, die folgende Liste zu sortieren: circle, square, rectangle, triangle, hexagon , konnten Sie nicht. Wenn Sie jedoch aufgefordert wurden, diese Liste anhand der Anzahl der Ecken zu sortieren, können Sie dies tun. Wenn Sie einen Komparator bereitstellen, wird Java angewiesen, zwei normalerweise nicht vergleichbare Objekte zu vergleichen.

public class PersonComparator implements Comparator<Person> {

    public int compare(Person p1, Person p2) {
        // If p1's lastName and p2's lastName are not comparably equivalent,
        // Compare p1 to p2 by comparing their last names.
        // Otherwise, compare p1 to p2 by comparing their first names
        if (p1.getLastName().compareTo(p2.getLastName()) != 0) {
            return p1.getLastName().compareTo(p2.getLastName());
        } else {
            return p1.getFirstName().compareTo(p2.getFirstName());
        }
    }
}

//Assume the first version of Person (that does not implement Comparable) is used here
public static void main(String[] args) {
    List<Person> people = Arrays.asList(new Person("John", "Doe"),
                                     new Person("Bob", "Dole"),
                                     new Person("Ronald", "McDonald"),
                                     new Person("Alice", "McDonald"),
                                     new Person("Jill", "Doe"));
    Collections.sort(people); //Illegal, Person doesn't implement Comparable.
    Collections.sort(people, new PersonComparator()); //Legal

    //people is now sorted by last name, then first name:
    // --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald
}

Komparatoren können auch als anonyme innere Klasse erstellt / verwendet werden

//Assume the first version of Person (that does not implement Comparable) is used here
public static void main(String[] args) {
    List<Person> people = Arrays.asList(new Person("John", "Doe"),
                                     new Person("Bob", "Dole"),
                                     new Person("Ronald", "McDonald"),
                                     new Person("Alice", "McDonald"),
                                     new Person("Jill", "Doe"));
    Collections.sort(people); //Illegal, Person doesn't implement Comparable.

    Collections.sort(people, new PersonComparator()); //Legal

    //people is now sorted by last name, then first name:
    // --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald

    //Anonymous Class
    Collections.sort(people, new Comparator<Person>() { //Legal
        public int compare(Person p1, Person p2) {
            //Method code...
        }
    });
}
Java SE 8

Vergleicher auf Lambda-Basis

Ab Java 8 können Komparatoren auch als Lambda-Ausdrücke ausgedrückt werden

    //Lambda
    Collections.sort(people, (p1, p2) -> { //Legal
        //Method code....
    });

Comparator-Standardmethoden

Darüber hinaus gibt es auf der Comparator-Benutzeroberfläche interessante Standardmethoden zum lastName von Komparatoren: Im Folgenden wird ein Komparator erstellt, der nach lastName und dann nach firstName .

Collections.sort(people, Comparator.comparing(Person::getLastName)
                                .thenComparing(Person::getFirstName));

Umkehren der Reihenfolge eines Komparators

Jeder Komparator kann auch leicht mit der reversedMethod wodurch die aufsteigende Reihenfolge in absteigend geändert wird.

Die compareTo und Compare-Methoden

Die Comparable<T> -Schnittstelle erfordert eine Methode:

public interface Comparable<T> {

    public int compareTo(T other);

}

Und die Comparator<T> -Schnittstelle erfordert eine Methode:

public interface Comparator<T> {

    public int compare(T t1, T t2);

}

Diese beiden Methoden tun im Wesentlichen die gleiche Sache, mit einem kleinen Unterschied: compareTo vergleicht this auf other , während compare vergleicht t1 bis t2 , nicht darum kümmern überhaupt über this .

Abgesehen von diesem Unterschied haben die beiden Methoden ähnliche Anforderungen. Spezifisch (für compareTo) Vergleicht dieses Objekt mit dem angegebenen Objekt für die Bestellung. Gibt eine negative Ganzzahl, Null oder eine positive Ganzzahl zurück, da dieses Objekt kleiner als, gleich oder größer als das angegebene Objekt ist. Für den Vergleich von a und b :

  • Wenn a < b , a.compareTo(b) und compare(a,b) eine negative Ganzzahl und b.compareTo(a) und compare(b,a) eine positive Ganzzahl zurückgeben sollen
  • Wenn a > b , sollten a.compareTo(b) und compare(a,b) eine positive Ganzzahl und b.compareTo(a) und compare(b,a) eine negative Ganzzahl zurückgeben
  • Wenn a gleich b zum Vergleich, sollten alle Vergleiche zurückkehren 0 .

Natürliche (vergleichbare) vs. explizite (Vergleicher) Sortierung

Es gibt zwei Collections.sort() -Methoden:

  • Eine, die eine List<T> als Parameter verwendet, in der T compareTo() implementieren muss, und die compareTo() -Methode überschreiben, die die compareTo() bestimmt.
  • Eine, die eine Liste und einen Vergleicher als Argument verwendet, wobei der Vergleicher die Sortierreihenfolge festlegt.

Hier ist zunächst eine Person-Klasse, die Comparable implementiert:

public class Person implements Comparable<Person> {         
    private String name;  
    private int age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }         

    @Override
    public int compareTo(Person o) {
        return this.getAge() - o.getAge();
    }
    @Override
    public String toString() {
        return this.getAge()+"-"+this.getName();
    }

}

So würden Sie die obige Klasse verwenden, um eine Liste in der natürlichen Reihenfolge ihrer Elemente zu sortieren, die durch die Methode compareTo() Methode überschrieben wird:

//-- usage
List<Person> pList = new ArrayList<Person>();
            Person p = new Person();
            p.setName("A");
            p.setAge(10);
            pList.add(p);
            p = new Person();
            p.setName("Z");
            p.setAge(20);
            pList.add(p);
            p = new Person();
            p.setName("D");
            p.setAge(30);
            pList.add(p);
            
            //-- natural sorting i.e comes with object implementation, by age
            Collections.sort(pList);

            System.out.println(pList);

So würden Sie einen anonymen Inline Comparator verwenden, um eine Liste zu sortieren, die Comparable nicht implementiert, oder in diesem Fall eine Liste in einer anderen Reihenfolge als der natürlichen Reihenfolge zu sortieren:

            //-- explicit sorting, define sort on another property here goes with name
            Collections.sort(pList, new Comparator<Person>() {

                @Override
                public int compare(Person o1, Person o2) {
                    return o1.getName().compareTo(o2.getName());
                }
            });            
            System.out.println(pList);

Karteneinträge sortieren

Ab Java 8 gibt es in der Map.Entry Schnittstelle Standardmethoden, um das Sortieren von Map.Entry zu ermöglichen.

Java SE 8
Map<String, Integer> numberOfEmployees = new HashMap<>();
numberOfEmployees.put("executives", 10);
numberOfEmployees.put("human ressources", 32);
numberOfEmployees.put("accounting", 12);
numberOfEmployees.put("IT", 100);

// Output the smallest departement in terms of number of employees
numberOfEmployees.entrySet().stream()
    .sorted(Map.Entry.comparingByValue())
    .limit(1)
    .forEach(System.out::println);   // outputs : executives=10

Natürlich können diese auch außerhalb der Stream-API verwendet werden:

Java SE 8
List<Map.Entry<String, Integer>> entries = new ArrayList<>(numberOfEmployees.entrySet());
Collections.sort(entries, Map.Entry.comparingByValue());

Erstellen eines Komparators mit der Vergleichsmethode

Comparator.comparing(Person::getName)

Dadurch wird ein Vergleicher für die Klasse Person , die diesen Personennamen als Vergleichsquelle verwendet. Es ist auch möglich, die Methodenversion zu verwenden, um long, int und double zu vergleichen. Zum Beispiel:

Comparator.comparingInt(Person::getAge)

Umgekehrte Reihenfolge

Um einen Komparator zu erstellen, der die umgekehrte Reihenfolge erzwingt, verwenden Sie die reversed() Methode:

Comparator.comparing(Person::getName).reversed()

Kette von Komparatoren

Comparator.comparing(Person::getLastName).thenComparing(Person::getFirstName)

Dadurch wird ein Vergleicher erstellt, der mit dem Nachnamen und dann mit dem Vornamen verglichen wird. Sie können beliebig viele Komparatoren verketten.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow