使用 ComparableT 或 ComparatorT 對列表進行排序

假設我們正在使用它們的名字和姓氏來代表一個 Person。我們已經建立了一個基本類來實現這一點並實現了正確的 equalshashCode 方法。

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

現在我們想按名稱對 Person 物件列表進行排序,例如在以下場景中:

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

不幸的是,如上所述,上面目前不會編譯。Collections.sort(..) 只知道如果列表中的元素具有可比性,或者給出自定義比較方法,如何對列表進行排序。

如果你被要求排序以下列表:1,3,5,4,2,你可以毫無疑問地說答案是 1,2,3,4,5。這是因為 Integers(在 Java 和數學上都有)具有自然排序,標準的預設比較基本排序。為了給我們的 Person 類一個自然的順序,我們實現 Comparable<Person>,這需要實現 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);
        }
    }
}

現在,給出的主要方法將正常執行

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
}

但是,如果你不想要或者不能修改類 Person,你可以提供一個自定義 Comparator<T> 來處理任何兩個 Person 物件的比較。如果你被要求對以下列表進行排序:circle, square, rectangle, triangle, hexagon 你不能,但如果你被要求根據角落的數量對該列表進行排序,你可以。就這樣,提供比較器指示 Java 如何比較兩個通常不可比較的物件。

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
}

比較器也可以建立/用作匿名內部類

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

Version >= Java SE 8

基於 Lambda 表示式的比較器

從 Java 8 開始,比較器也可以表示為 lambda 表示式

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

比較器預設方法

此外,Comparator 介面上有一些有趣的預設方法用於構建比較器:下面構建一個比較器,通過 lastNamefirstName 進行比較。

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

顛倒比較器的順序

任何比較器也可以使用 reversedMethod 輕鬆反轉,reversedMethod 將升序降序為降序。