1. 程式人生 > 實用技巧 >Comparator比較器

Comparator比較器

Comparator比較器

簡介

為什麼寫?


  • comparatorjavase中的介面,位於java.util包下,該介面抽象度極高,有必要掌握該介面的使用
  • 大多數文章告訴大家comparator是用來排序,但我想說排序是comparator能實現的功能之一,他不僅限於排序

介面功能


Comparator介面代表一個比較器,比較器具有可比性!平時我們大多數都是使用改介面(Comparator)實現對集合,排序。這是
因為JAVASE陣列工具類和集合工具類中提供的sort方法sort就是使用Comparator介面來處理排序的,但是Comparator介面並不只是
用來排序的,下面是JAVASE一些使用到Comparator介面的地方:

Arrays.sort(T[],Comparator<? super T> c);
Collections.sort(List<T> list,Comparator<? super T> c);

使用場景


什麼場景需要做比較,那麼什麼場景就是Comparator介面的用武之地,我總結的兩個場景:

  1. 排序,需要比較兩個物件誰排在前誰排在後(排序也可以讓類實現Comparable介面,實現後該類的例項也具有排序能力)。
  2. 分組,需要比較兩個物件是否是屬於同一組。

排序場景

在List或陣列中的物件如果沒有實現Comparable介面時,那麼就需要呼叫者為需要排序的陣列或List設定一個Compartor,Compartor的compare方法用來告訴程式碼應該怎麼去比較兩個例項,然後根據比較結果進行排序。

條件排序:公共程式碼

public class SortTest {

    @Data
    @AllArgsConstructor
    @ToString
    class Dog{ //內部類
        public int age;
        public String name;
        public String num;
    }

    List<Dog> list= new ArrayList<Dog>(){
        {
            add(new Dog(5, "DogA","001"));
            add(new Dog(5, "DogB","002"));
            add(new Dog(5, "DogC","003"));
            add(new Dog(9, "DogA","004"));
            add(new Dog(35, "DogF","005"));
            add(new Dog(74, "Dogg","006"));
        }
    };
}

單一條件排序

Comparator實現排序(按照年齡,名字)

   /**
     *單一條件排序
     */
    
    @Test
    public void test1(){
        //按照年齡排序
        Collections.sort(list, new Comparator<Dog>() {
            //實現compare(T o1, To2) 方法,返回正數,零,負數各代表大於,等於小於
            @Override
            public int compare(Dog o1, Dog o2) {
                //return o2.age - o1.age; //排序規則----升序
                return String.valueOf(o1.getAge()).compareTo(String.valueOf(o2.getAge())); //compareTo()傳String
            }
        });
        System.out.println("給狗狗按照年齡倒序:"+list);
    }

    @Test
    public void test2(){
        //按照名字排序
        Collator comparator = Collator.getInstance(Locale.CANADA);

        Collections.sort(list, new Comparator<Dog>() {

            @Override
            public int compare(Dog o1, Dog o2) {
               // return o1.name.compareTo(o2.name);
                return comparator.compare(o1.getName(),o2.getName());
            }
        });
        System.out.println("給狗狗按名字字母順序排序:"+list);
    }

Lambda優化實現排序

    /**
     *使用Lambda表示式優化比較器程式碼(單一條件排序)
     */
    @Test
    public void test3() {
        //對學生集合按年齡進行排序
        Collections.sort(list,(s1, s2) -> (s1.getAge() - s2.getAge()) );
    }

多條件排序

Comparator實現排序(按照年齡和名字)

 	/**
     *多條件排序
     */
    @Test
    public void test4() {

        Collections.sort(list,new Comparator<Dog>() {

            @Override
            public int compare(Dog s1, Dog s2) {
                int flag;
                // 首選按年齡升序排序
                flag = s1.getAge()-s2.getAge();
                if(flag==0){
                    // 如果年齡按編號降序排序
                    flag = s2.getNum().compareTo(s1.getNum());
                }
                return flag;
            }
        });
        list.forEach(System.out::println);
       }

Lambda優化實現多條件排序

       /**
        *多條件排序 ----使用lambda表示式優化
        */
    @Test
    public void test5() {
        Collections.sort(list,(s1,s2)->{
            int flag;
            // 首選按年齡升序排序
            flag = s1.getAge()-s2.getAge();

            // 方案一、判斷是否為空 --- 為空執行lambda表示式,返回一個物件
            flag = Optional.ofNullable(flag == 0 ? null: flag).orElseGet(() -> s2.getNum().compareTo(s1.getNum()));

            //方案二、
            if(flag==0){
                // 如果年齡按編號降序排序
                flag =s2.getNum().compareTo(s1.getNum());
            }
            //最終返回
            return flag;
        });
        list.forEach(System.out::println);
    }

自定義條件排序

自定義條件排序公共程式碼


    /**
     *自定義條件排序
     */
    //定義排序規則  通過asList()方法將陣列轉為list集合
    String[] order = {"語文","數學","英語","物理","化學","生物","政治","歷史","地理","總分"};
    final List<String> definedOrder = Arrays.asList(order);

    //需要排序的資料
    List<String> listClass = new ArrayList<String>(){
        {
            add("總分");
            add("英語");
            add("政治");
            add("總分");
            add("數學");
        }
    };

自定義條件排序方案一 Comparator

    //自定義條件排序方案一
    @Test
    public void test6(){

        Collections.sort(listClass,new Comparator<String>() {

            @Override
            public int compare(String o1, String o2) {
                //int indexOf(String str) :返回第一次出現的指定子字串在此字串中的索引。
                int io1 = definedOrder .indexOf(o1);
                int io2 = definedOrder .indexOf(o2);
                return io1-io2;
            }
        });

        for(String s:listClass){
            System.out.print(s+"   ");
        }
        //列印結果:數學   英語   政治   總分   總分
    }

自定義條件排序方案二 Lambda

    //自定義條件排序方案二 使用Lambda表示式優化比較器程式碼
    @Test
    public void test7(){
        Collections.sort(listClass,(s1,s2)->definedOrder.indexOf(s1) - definedOrder.indexOf(s2));
        listClass.forEach(System.out::print);
    }

分組場景


使用Comparatorfor迴圈處理列表,來進行分類;通過呼叫者實現Comparator介面的比較邏輯,來告訴程式應該怎麼比較,通過比較之後得結果來進行分組。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
下面例子中分別按照狗狗的顏色和體重級別兩個維度來進行分組,因此分組的核心邏輯其實就是比較邏輯。相面我抽了一個工具方法:dividerList,第一個引數為需要處理的資料來源,第二引數是分組時的比較邏輯。

公共程式碼

  @Data
    @AllArgsConstructor
    @ToString
    class Apple {
        public String color;
        public int weight;
    }
    List<Apple> list = new ArrayList<Apple>(){
        {
            add(new Apple("紅", 205));
            add(new Apple("紅", 131));
            add(new Apple("綠", 248));
            add(new Apple("綠", 22));
            add(new Apple("黃", 119));
            add(new Apple("黃", 224));
            add(new Apple("白", 2024));
        }
    };

    /**
     *   是否為同一組的判斷標準        引數一:需要處理的資料來源  datas,引數二分組時的比較邏輯c
     */
    public static <T> List<List<T>> divider(Collection<T> datas, Comparator<? super T> c) {
        List<List<T>> result = new ArrayList<List<T>>();
        for (T t : datas) {
            boolean isSameGroup = false;
            for (int j = 0; j < result.size(); j++) {
                if (c.compare(t, result.get(j).get(0)) == 0) {
                    isSameGroup = true;
                    result.get(j).add(t);
                    break;
                }
            }
            if (!isSameGroup) {
                // 建立
                List<T> innerList = new ArrayList<T>();
                result.add(innerList);
                innerList.add(t);
            }
        }
        return result;
    }

Comparator實現按顏色分組

    @Test
    public void test1(){
        List<List<Apple>> byColors = divider(list, new Comparator<Apple>() {
            //按照顏色基本進行分組
            @Override
            public int compare(Apple o1, Apple o2) {
                // 按顏色分組
                return o1.color.compareTo(o2.color);
            }
        });
        System.out.println("按顏色分組" + byColors);
    }

Lambda優化按顏色分組

    /**
     *使用lambda優化(按照顏色分組)
     */
   @Test
   public void test1_1() {
       //按照顏色重量級分組
       divider_Lambda(list, (o1, o2) -> o1.color.compareTo(o2.color)).forEach(System.out::println);
   }

Comparator實現按分組

    @Test
    public void test2(){
        //按照重量級進行分組
        List<List<Apple>> byWeight = divider(list, new Comparator<Apple>() {

            @Override
            public int compare(Apple o1, Apple o2) {
                // 按重量級
                return (o1.getWeight() / 100 == o2.getWeight() / 100) ? 0 : 1;
            }
        });
        byWeight.forEach(x-> System.out.println("按照總量分組:"+x));
        //System.out.println("按重量級分組" + byWeight);
    }

Lambda優化按重量級分組

    /**
     *使用lambda優化(按照重量級進行分組)
     */
    @Test
    public void test2_1() {
        //按照顏色分組
        divider_Lambda(list, (o1, o2) -> (o1.getWeight() / 100 == o2.getWeight() / 100) ? 0 : 1).forEach(System.out::println);
    }

Comparator介面常用的方法

公共程式碼

實體類

Persons實體類

package com.zy.pagehelper.model;

import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;

@Data
@NoArgsConstructor
@Builder
public class Persons implements Comparable {
    private String name;
    private BigDecimal age;
    private Integer height;
    private Student student;

    public Persons(String name, BigDecimal age, Integer height) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.student = new Student(0);
    }

    public Persons(String name, BigDecimal age, Integer height, Student student) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.student = student;
    }


    @Override
    public int compareTo(Object o) {
        Persons p1 = (Persons) o;

        if (this.age.equals(p1.age)) {
            return p1.height - this.height;
        }
        return this.age.compareTo(p1.age);
    }
}

Student實體類

package com.zy.pagehelper.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Comparator;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student implements Comparator {

    private int age;

    @Override
    public int compare(Object o1, Object o2) {

        Student p1 = (Student) o1;
        Student p2 = (Student) o2;

        int result = Integer.compare(p1.age, p2.age);

        result = result == 0 ? ((p1.age > p2.age) ? 1 : -1) : result;

        return result;
    }
}

公共集合

    Persons persons = new Persons();
    List<Persons> personList = new ArrayList<Persons>() {
        {
            add(new Persons("a", new BigDecimal(12), 170));
            add(new Persons("b", new BigDecimal(24), 175, new Student(27)));
            add(new Persons("c", new BigDecimal(12), 177));
            add(new Persons("a", new BigDecimal(12), 177));
            add(new Persons("b", new BigDecimal(54), 174, new Student(19)));
        }
    };

naturalOrder 方法


naturalOrder - 自然比較,根據實體類定義的Comparable進行比較!

示例程式碼:

    @Test
    public void testNaturalOrder(){
        // naturalOrder  自然比較,根據實體類定義的Comparable
        System.out.println("naturalOrder : ");
        personList.sort(Comparator.naturalOrder());
        personList.forEach(System.out::println);
    }

comparing方法


comparing、comparingLong、comparingInt、comparingDouble - 常用比較方法,可以指定引數型別
comparing方法引數是一個函式式介面keyExtractor,意識即為指定排序物件中的排序鍵,這裡注意排序鍵這裡標註了Comparable介面
comparing方法

    /**
     * Accepts a function that extracts a {@link java.lang.Comparable
     * Comparable} sort key from a type {@code T}, and returns a {@code
     * Comparator<T>} that compares by that sort key.
     *
     * <p>The returned comparator is serializable if the specified function
     * is also serializable.
     *
     * @apiNote
     * For example, to obtain a {@code Comparator} that compares {@code
     * Person} objects by their last name,
     *
     * <pre>{@code
     *     Comparator<Person> byLastName = Comparator.comparing(Person::getLastName);
     * }</pre>
     */
    public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor)
    {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
    }

comparing方法引數是一個函式式介面keyExtractor,意識即為指定排序物件中的排序鍵,這裡注意排序鍵這裡標註了Comparable介面。
同時我們也可以看到有過載的comparing方法:

   /**
     * Accepts a function that extracts a sort key from a type {@code T}, and
     * returns a {@code Comparator<T>} that compares by that sort key using
     * the specified {@link Comparator}.
      *
     * <p>The returned comparator is serializable if the specified function
     * and comparator are both serializable.
     *
     * @apiNote
     * For example, to obtain a {@code Comparator} that compares {@code
     * Person} objects by their last name ignoring case differences,
     *
     * <pre>{@code
     *     Comparator<Person> cmp = Comparator.comparing(
     *             Person::getLastName,
     *             String.CASE_INSENSITIVE_ORDER);
     * }</pre>
     */
  public static <T, U> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor,
            Comparator<? super U> keyComparator)
    {
        Objects.requireNonNull(keyExtractor);
        Objects.requireNonNull(keyComparator);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
                                              keyExtractor.apply(c2));
    }

第二個引數也很好理解,提取完sort key之後,要定義關於這個key的Comparator,在註釋中的例子也比較好理解。 這裡有個小tips:在String類中,提供了一個實現Comparator介面的常量來標識不對語言敏感的字典序排序器。

 /**
     * A Comparator that orders {@code String} objects as by
     * {@code compareToIgnoreCase}. This comparator is serializable.
     * <p>
     * Note that this Comparator does <em>not</em> take locale into account,
     * and will result in an unsatisfactory ordering for certain locales.
     * The java.text package provides <em>Collators</em> to allow
     * locale-sensitive ordering.
     *
     * @see     java.text.Collator#compare(String, String)
     * @since   1.2
     */
    public static final Comparator<String> CASE_INSENSITIVE_ORDER
                                         = new CaseInsensitiveComparator();
    private static class CaseInsensitiveComparator
            implements Comparator<String>, java.io.Serializable {
        // use serialVersionUID from JDK 1.2.2 for interoperability
        private static final long serialVersionUID = 8575799808933029326L;

        public int compare(String s1, String s2) {
            int n1 = s1.length();
            int n2 = s2.length();
            int min = Math.min(n1, n2);
            for (int i = 0; i < min; i++) {
                char c1 = s1.charAt(i);
                char c2 = s2.charAt(i);
                if (c1 != c2) {
                    c1 = Character.toUpperCase(c1);
                    c2 = Character.toUpperCase(c2);
                    if (c1 != c2) {
                        c1 = Character.toLowerCase(c1);
                        c2 = Character.toLowerCase(c2);
                        if (c1 != c2) {
                            // No overflow because of numeric promotion
                            return c1 - c2;
                        }
                    }
                }
            }
            return n1 - n2;
        }

        /** Replaces the de-serialized object. */
        private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
    }


// 這裡其實可以看到compareToIgnoreCase也是呼叫了這個例項的compare方法
public int compareToIgnoreCase(String str) {
        return CASE_INSENSITIVE_ORDER.compare(this, str);
    }

在Comparator介面中,也直接提供了具體型別的三個comparing方法:

public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
    }


public static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor) {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2));
    }
    
     public static<T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor) {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2));
    }

測試示例程式碼:

    @Test
    public void testComparing(){
        //comparing、comparingLong、comparingInt、comparingDouble - 常用比較方法,可以指定引數型別

        // comparing 1.0  比較集合中物件的年齡,取最大值
        Optional<Persons> optional = personList.stream().max(Comparator.comparing(Persons::getAge));
        System.out.println("comparing 1.0 : get max age " + optional.get().toString() + "\n");

        // comparing 2.1
        optional = personList.stream().max(Comparator.comparing(Persons::getName, Comparator.reverseOrder()));
        System.out.println("comparing 2.1 : get min name " + optional.get().toString() + "\n");

        // comparing 2.2
        optional = personList.stream().max(Comparator.comparing(Persons::getName, String::compareTo));
        System.out.println("comparing 2.2 : get max name " + optional.get().toString() + "\n");

        // comparing 2.3        該方法多了一個引數 keyComparator ,keyComparator 是建立一個自定義的比較器。示例種:通過cmmpare()方法進行學生年齡比較,
        optional = personList.stream().max(Comparator.comparing(Persons::getStudent, (o1, o2) -> new Student().compare(o1, o2)));
        System.out.println("comparing 2.3 : get max student.age " + optional.get().toString() + "\n");

    }
/*
列印結果
 comparing 1.0 : get max age Persons(name=b, age=54, height=174, student=Student(age=19))

 comparing 2.1 : get min name Persons(name=a, age=12, height=170, student=Student(age=0))

 comparing 2.2 : get max name Persons(name=c, age=12, height=177, student=Student(age=0))

 comparing 2.3 : get max student.age Persons(name=b, age=24, height=175, student=Student(age=27)) 
*/  
    
        // 升序 comparing方法的具體實現一comparingInt
        System.out.println("升序 : ");
        personList.sort(Comparator.comparingInt(Persons::getHeight));
        personList.forEach(System.out::println);


        // 降序 comparing方法的具體實現二comparingInt
        System.out.println("降序 : ");
        personList.sort(Comparator.comparingInt(Persons::getHeight).reversed());
        personList.forEach(System.out::println);
   

thenComparing方法


/**
     * Returns a lexicographic-order comparator with another comparator.
     * If this {@code Comparator} considers two elements equal, i.e.
     * {@code compare(a, b) == 0}, {@code other} is used to determine the order.
     *
     * <p>The returned comparator is serializable if the specified comparator
     * is also serializable.
     *
     * @apiNote
     * For example, to sort a collection of {@code String} based on the length
     * and then case-insensitive natural ordering, the comparator can be
     * composed using following code,
     *
     * <pre>{@code
     *     Comparator<String> cmp = Comparator.comparingInt(String::length)
     *             .thenComparing(String.CASE_INSENSITIVE_ORDER);
     * }</pre>
     */
    default Comparator<T> thenComparing(Comparator<? super T> other) {
        Objects.requireNonNull(other);
        return (Comparator<T> & Serializable) (c1, c2) -> {
            int res = compare(c1, c2);
            return (res != 0) ? res : other.compare(c1, c2);
        };
    }

從方法名稱上知道這是當比較相同時的使用的一個排序規則,這裡需要注意看具體實現是會先呼叫比較器例項中的compare方法來進行比較一輪,當結果等於0的時候才會呼叫other這個比較器規則進行比較。比如下面的一個DOME:

List<String> strings = Arrays.asList("def", "abc", "hel", "world");
        strings.sort(Comparator.comparingInt(String::length).reversed() //(1)
                .thenComparing(String::compareToIgnoreCase) // (2)
                .thenComparing(Comparator.reverseOrder()) // (3)這個比較器不會被應用 因為比較器(2)已經把結果比較出來了,並且沒有相等的結果,這裡不會再應用(3)比較器
        );

        System.out.println(strings); // 輸出[world, abc, def, hel]

當然因為有了 comparing方法的支援,所以也就有了下面兩個thenComparing的過載方法

  default <U extends Comparable<? super U>> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor)
    {
        return thenComparing(comparing(keyExtractor));
    }
 default <U> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor,
            Comparator<? super U> keyComparator)
    {
        return thenComparing(comparing(keyExtractor, keyComparator));
    }

測試示例程式碼:

    @Test
    public void test2(){
        //根據第一個屬性進行排序,如果相同則以此根據下一個thenComparing()中的屬性進行排序

        // thenComparing 1.0
        System.out.println("thenComparing 1.0 : ");
        personList.sort(Comparator.comparing(Persons::getAge));
        personList.forEach(System.out::println);

        // thenComparing 1.1
        System.out.println("thenComparing 1.1 : ");
        personList.sort(Comparator.comparing(Persons::getAge).thenComparing(Persons::getHeight));
        personList.forEach(System.out::println);

        // thenComparing 2.0
        System.out.println("thenComparing 2.0 : ");
        personList.sort(Comparator.comparing(Persons::getAge).thenComparing(Persons::getHeight).thenComparing(Persons::getName));
        personList.forEach(System.out::println);

    }

nullsLast()/nullsFirst()


Comparator介面中有兩個對null友好的比較器方法:

   /**
     * Returns a null-friendly comparator that considers {@code null} to be
     * less than non-null. When both are {@code null}, they are considered
     * equal. If both are non-null, the specified {@code Comparator} is used
     * to determine the order. If the specified comparator is {@code null},
     * then the returned comparator considers all non-null values to be equal.
     *
     * <p>The returned comparator is serializable if the specified comparator
     * is serializable.
     *
     * @param  <T> the type of the elements to be compared
     * @param  comparator a {@code Comparator} for comparing non-null values
     * @return a comparator that considers {@code null} to be less than
     *         non-null, and compares non-null objects with the supplied
     *         {@code Comparator}.
     * @since 1.8
     */
    public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
        return new Comparators.NullComparator<>(true, comparator);
    }
    
    
    // null比非null元素都大的
     public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) {
        return new Comparators.NullComparator<>(false, comparator);
    }

這裡是通過Comparators這個工廠類提供的NullComparator比較器實現的,看到註釋有一條需要注意是如果不指定comparator引數,即傳入null,那麼所有的非null引數都會被視為相等。

當集合中存在null元素時,可以使用針對null友好的比較器,null元素排在集合的最前面/最後面
測試示例程式碼

    @Test
    public void testNulls() {

        // nullsLast
        System.out.println("nullsLast : ");
        personList.sort(Comparator.nullsLast(Comparator.comparing(Persons::getName)));
        personList.forEach(System.out::println);

        // nullsFirst
        System.out.println("nullsFirst : ");
        personList.sort(Comparator.nullsFirst(Comparator.comparing(Persons::getName)));
        personList.forEach(System.out::println);
    }

Comparator介面和Comparable介面

這兩個介面首先要做一個簡單區別。

Comparable介面

* Lists (and arrays) of objects that implement this interface can be sorted
 * automatically by {@link Collections#sort(List) Collections.sort} (and
 * {@link Arrays#sort(Object[]) Arrays.sort}).  Objects that implement this
 * interface can be used as keys in a {@linkplain SortedMap sorted map} or as
 * elements in a {@linkplain SortedSet sorted set}, without the need to
 * specify a {@linkplain Comparator comparator}.<p>

可以看到註釋中說明了實現了該介面的物件,在陣列中可以使用Collections.sort或者Arrays.sort方法實現排序,或者實現了該介面的物件可以作為sortedMap或者SortedSet的key。這裡也提到我們不用制定一個排序或者作為key的Comparator介面。

public interface Comparable<T> {
    /**
     * 省略部分註釋
     * <p>The implementor must ensure <tt>sgn(x.compareTo(y)) ==
     * -sgn(y.compareTo(x))</tt> for all <tt>x</tt> and <tt>y</tt>.  (This
     * implies that <tt>x.compareTo(y)</tt> must throw an exception iff
     * <tt>y.compareTo(x)</tt> throws an exception.)
     */
    public int compareTo(T o);
}

compareTo方法上的註釋中提到,必須確保 x.compareTo(y)y.compareTo(x)的結果是一致的,並且這也意味著當x.compartTo(y)丟擲一個異常,那麼y.compareTo(x)也應該去丟擲一個異常,那麼這裡就思考到了一個關於null的設計:null.compareTo(obj)我們肯定知道會有NPE,那麼你在實現compareTo方法的時候,如果obj.compareTo(null)這裡也應該去丟擲NPE。

這裡就不去寫具體的demo去演示了,這裡理解為一個物件實現了Comparable介面,那麼這個物件就是可比較的,並且在排序等場景下呼叫實現介面中的compareTo方法。

Comparator介面

Comparator介面要理解為比較器,實現其介面的類其實是比較器的一種實現,相當於一個比較的函式定義。來看下他的註釋:

* A comparison function, which imposes a <i>total ordering</i> on some
 * collection of objects.  Comparators can be passed to a sort method (such
 * as {@link Collections#sort(List,Comparator) Collections.sort} or {@link
 * Arrays#sort(Object[],Comparator) Arrays.sort}) to allow precise control
 * over the sort order.  Comparators can also be used to control the order of
 * certain data structures (such as {@link SortedSet sorted sets} or {@link
 * SortedMap sorted maps}), or to provide an ordering for collections of
 * objects that don't have a {@link Comparable natural ordering}.<p>

這裡我們看到Arrays、Collections也提供了過載的sort方法,支援傳入一個集合/陣列和Comparator介面的例項。當然當前列表/陣列中的物件不一定是實現了Comparable介面。

類實現了comparable介面之後,可以直接呼叫排序方法;而當使用comparator時,不需要類實現,具體使用時(也就是呼叫某些方法時)的需要類和該comparator繫結起來來實現。comparable實現內部排序,Comparator是外部排序。