Sök…


Syntax

  • public class MyClass implementerar Jämförbar <MyClass >
  • offentlig klass MyComparator implementerar Comparator <SomeOtherClass >
  • public int jämförTo (MyClass övrigt)
  • public int jämför (SomeOtherClass o1, SomeOtherClass o2)

Anmärkningar

När du implementerar en compareTo(..) -metod som beror på en double , gör inte följande:

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

Trunkeringen orsakad av (int) cast kommer att göra att metoden ibland felaktigt returnerar 0 istället för ett positivt eller negativt tal, och kan således leda till jämförelse och sortering av buggar.

Istället är den enklaste korrekta implementeringen att använda Double.compare , som sådan:

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

En icke-generisk version av Comparable<T> , helt enkelt Comparable , har funnits sedan Java 1.2 . Annat än för att gränssnitt med äldre kod, är det alltid bättre att implementera den generiska versionen Comparable<T> , eftersom den inte kräver gjutning vid jämförelse.


Det är mycket standard för en klass att vara jämförbar med sig själv, som i:

public class A implements Comparable<A>

Det är möjligt att bryta sig från detta paradigm, men var försiktig när du gör det.


En Comparator<T> kan fortfarande användas i instanser av en klass om den klassen implementerar Comparable<T> . I detta fall kommer Comparator logik att användas; den naturliga beställningen som specificeras av den Comparable implementeringen ignoreras.

Sortera en lista med jämförbar eller en komparator

Säg att vi arbetar på en klass som representerar en person med sina för- och efternamn. Vi har skapat en grundläggande klass för att göra detta och implementerat korrekt equals och hashCode metoder.

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);
    }
}

Nu vill vi sortera en lista med Person efter deras namn, till exempel i följande scenario:

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.
}

Tyvärr, som markerat, kommer ovanstående för närvarande inte att kompilera. Collections.sort(..) vet bara hur man sorterar en lista om elementen i listan är jämförbara eller om en anpassad jämförelsemetod ges.

Om du ombads att sortera följande lista: 1,3,5,4,2 , har du inga problem att säga att svaret är 1,2,3,4,5 . Detta beror på att heltal (både i Java och matematiskt) har en naturlig beställning , en standardbeställning av standardjämförelse. För att ge vår personklass en naturlig beställning implementerar vi Comparable<Person> , som kräver implementering av metoden 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);
        }
    }
}

Nu kommer den angivna huvudmetoden att fungera 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
}

Om du emellertid inte vill eller inte kan ändra klass Person kan du tillhandahålla en anpassad Comparator<T> som hanterar jämförelsen mellan två Person objekt. Om du ombads att sortera följande lista: circle, square, rectangle, triangle, hexagon kunde du inte, men om du blev ombedd att sortera den listan baserat på antalet hörn kunde du göra det. Bara så, genom att ge en komparator instruerar Java hur man kan jämföra två normalt inte jämförbara objekt.

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
}

Jämförare kan också skapas / användas som en anonym inre klass

//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

Lambda-uttrycksbaserade komparatorer

Från Java 8 kan komparatorer också uttryckas som lambda-uttryck

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

Comparator standardmetoder

Dessutom finns det intressanta standardmetoder i Comparator-gränssnittet för att bygga komparatorer: följande bygger en komparator som jämför med lastName och sedan firstName .

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

Invertera ordningen för en komparator

Vilken som helst komparator kan också enkelt vändas med reversedMethod som kommer att ändra stigande ordning till fallande.

Jämför & För att jämföra metoder

Comparable<T> gränssnitt kräver en metod:

public interface Comparable<T> {

    public int compareTo(T other);

}

Och gränssnittet Comparator<T> kräver en metod:

public interface Comparator<T> {

    public int compare(T t1, T t2);

}

Dessa två metoder gör i huvudsak samma sak, med en mindre skillnad: jämför compareTo jämföra this med other , medan compare jämför t1 till t2 , och bryr sig inte alls om this .

Bortsett från denna skillnad har de två metoderna liknande krav. Specifikt (för jämförTo) Jämför detta objekt med det angivna objektet för ordning. Returnerar ett negativt heltal, noll eller ett positivt heltal eftersom detta objekt är mindre än, lika med eller större än det angivna objektet. För jämförelse av a och b :

  • Om a < b , a.compareTo(b) och compare(a,b) ska returnera ett negativt heltal, och b.compareTo(a) och compare(b,a) ska returnera ett positivt heltal
  • Om a > b , a.compareTo(b) och compare(a,b) ska returnera ett positivt heltal, och b.compareTo(a) och compare(b,a) ska returnera ett negativt heltal
  • Om a lika med b för jämförelse bör alla jämförelser returnera 0 .

Naturlig (jämförbar) vs uttrycklig (komparator) sortering

Det finns två Collections.sort() metoder Collections.sort() :

  • En som tar en List<T> som en parameter där T måste implementera Jämförbar och åsidosätta compareTo() som bestämmer sorteringsordning.
  • En som tar en lista och en komparator som argument, där komparatorn bestämmer sorteringsordningen.

Först här är en personklass som implementerar Jämförbar:

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();
    }

}

Så här använder du klassen ovan för att sortera en lista i den naturliga ordningen av dess element, definierad av compareTo() åsidosättande:

//-- 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);

Så här använder du en anonym inline Comparator för att sortera en lista som inte implementerar Jämförbar, eller i detta fall, för att sortera en lista i en annan ordning än den naturliga beställningen:

            //-- 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);

Sortera kartposter

Från Java 8 finns det standardmetoder på Map.Entry gränssnittet för att tillåta sortering av kart-iterationer.

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

Naturligtvis kan dessa också användas utanför strömmen api:

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

Skapa en komparator med jämförande metod

Comparator.comparing(Person::getName)

Detta skapar en komparator för klassen Person som använder detta personnamn som jämförelsekälla. Det är också möjligt att använda metodversion för att jämföra lång, int och dubbel. Till exempel:

Comparator.comparingInt(Person::getAge)

Omvänd ordning

För att skapa en komparator som påför omvänd ordning använder reversed() metod:

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

Kedja av komparatorer

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

Detta skapar en komparator som jämför jämförelse med efternamn och jämför sedan med förnamn. Du kan kedja så många komparatorer som du vill.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow