1. 程式人生 > >Comparator和Comparable之間的區別

Comparator和Comparable之間的區別

簡介

Comparable介面

該介面對每個實現它的類的物件強加了排序規則。該排序稱之為自然排序(natural ordering)。方法public int compareTo(T o)是自然排序的排序方法。

實現了comparable介面類的List或者arrays物件可以通過呼叫以下方法進行排序:

1. Collections#sort(List<T> list) //內部是呼叫第二個方法進行排序
2.  Arrays#sort(Object[] a)       //演算法是合併排序或二分排序 n^2演算法複雜度

//此外,可以通過將元素新增到一下集合或Map中實現排序
3. SortedMap tm = new TreeMap(); 4. SortedSet ts = new TreeSet();

自然排序中,e1.compareTo(e2) == 0的布林值應該等價於e1.equals(e2)。注意null不屬於任何類的例項,故e.compareTo(null)應丟擲空指標異常,儘管此時e.equals(null)false

實際上,所有實現comparable的類中方法compareToequals方法值是等價的。一個特例就是java.math.BigDecimal

Comparator介面

通過比較兩個入參得到順序。返回值有三種:

  • 1,入參一大於入參二。
  • 0,入參相同。
  • -1,入參一小於入參二。

實現的類必須保證以下 要求:

  1. sgn(compare(x, y)) == -sgn(compare(y, x))。暗示著compare(x, y)拋異常,則compare(y,x)也拋異常。
  2. ((compare(x, y) < 0),((compare(y,z) < 0)暗示著((compare(x,z) < 0)
  3. compare(x, y)==0暗示著sgn(compare(x, z))==sgn(compare(y, z))

通常情況下,實現Comparator的類並不被嚴格要求要遵循(compare(x, y)==0) == (x.equals(y))

,但是違反該條件的應該明確地指出這一事實,即說明條件(compare(x, y)==0) == (x.equals(y))並不成立。

實現Comparator的介面的List或者arrays物件可以通過呼叫以下方法進行排序:

1. Collections#sort(List<T> list,, Comparator<? super T> c) //內部是呼叫第二個方法進行排序
2.  Arrays#sort(Object[] a, Comparator<? super T> c)       //演算法是合併排序或二分排序 n^2演算法複雜度

//此外還可以通過將物件加入以下集合或Map實現排序
3. SortedMap tm = new TreeMap(Comparator<? super K> comparator);
4. SortedSet ts = new TreeSet(Comparator<? super K> comparator);

如果有多個比較規則,可以充分利用Comparator介面的預設方法:

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

Comparator和Comparable的區別

引數 Comparable Comparator
排序邏輯 排序邏輯必須在待排序物件的類中,故稱之為自然排序 排序邏輯在另一個實現
實現 實現Comparable介面 實現Comparator介面
排序方法 int compareTo(Object o1) int compare(Object o1,Object o2)
觸發排序 Collections.sort(List) Collections.sort(List, Comparator)
介面所在包 java.lang.Comparable java.util.Comparator

栗子

假設我們想要人類先以姓名升序排序,假如姓名一樣則按照年齡排序,則程式碼可以如下:
Person.java

@Data
@NoArgsConstructor
@AllArgsConstructor
/**
 * @author hjb
 */
public class Person  implements Comparable {
    private String name;
    private Integer age;
    private String mind;
    @Override
    public int compareTo(Object o) {
        Person targetPerson = (Person)o;
        int ret = this.name.compareTo(targetPerson.getName());
        if(ret == 0){
            return Integer.compare(this.age,targetPerson.getAge());
        }
        if(ret == 0){
            return this.getMind().compareTo(targetPerson.getMind());
        }
        return ret;
    }
}

PersonComparator.java

public class PersonComparator implements Comparator<Person> {
    @Override
    public int compare(Person p, Person p2) {

        int ret = p.getName().compareTo(p2.getName());
        if(ret == 0){
            return p.getAge().compareTo(p2.getAge());
        }
        return ret;
    }
}

PersonComparator2.java

public class PersonComparator2 implements Comparator<Person> {
    @Override
    public int compare(Person p, Person p2) {
         return p.getMind().compareTo(p2.getMind());
    }
}

TestCompare.java

public class TestComparable {

    public static void main(String[] args){
        //取資料
        List<Person> personList = generatePersonList();
        //排序一: 正常
        Collections.sort(personList);
        print("Collections.sort(list)",personList);
        //排序二:發現正常了
        PersonComparator pc = new PersonComparator();
        PersonComparator2 pc2 = new PersonComparator2();
        Collections.sort(personList,pc.thenComparing(pc2));
        print("thenComparing",personList);
        //排序三:正常
        SortedSet<Person> ts = new TreeSet<>(personList);
        print("TreeSet",ts);
        //排序四:發現某個Person例項被吃掉了,因為TreeSet認為CompareTo為0,則兩個Person例項相同
        SortedSet<Person> ts2 = new TreeSet<>(new PersonComparator());
        ts2.addAll(personList);
        print("TreeSet(Comparator)",ts2);
    }

    //生成待排序陣列
    private static List<Person> generatePersonList() {
        List<Person> retList = new ArrayList<>(16);
        retList.addAll(
                Arrays.asList(
                        new Person[]{
                                new Person("erMaZi", 19, "good"),
                                new Person("liSi", 17, "bad"),
                                new Person("wangWu", 18, "middle"),
                                new Person("wangWu", 18, "middla"),
                        }
                )
        );
        return retList;
    }
    public static void print(String message,List<Person> personList){
        System.out.println(message+":");
       for(Person p:personList){
           System.out.println(p);
       }
        System.out.println();
    }
    public static void print(String message,SortedSet<Person> sortedSet){
        System.out.println(message+":");
        for(Person p:sortedSet){
            System.out.println(p);
        }
        System.out.println();
    }
}

Output

Collections.sort(list):
Person(name=erMaZi, age=19, mind=good)
Person(name=liSi, age=17, mind=bad)
Person(name=wangWu, age=18, mind=middla)
Person(name=wangWu, age=18, mind=middle)

thenComparing:
Person(name=erMaZi, age=19, mind=good)
Person(name=liSi, age=17, mind=bad)
Person(name=wangWu, age=18, mind=middla)
Person(name=wangWu, age=18, mind=middle)

TreeSet:
Person(name=erMaZi, age=19, mind=good)
Person(name=liSi, age=17, mind=bad)
Person(name=wangWu, age=18, mind=middla)
Person(name=wangWu, age=18, mind=middle)

TreeSet(Comparator):
Person(name=erMaZi, age=19, mind=good)
Person(name=liSi, age=17, mind=bad)
Person(name=wangWu, age=18, mind=middla)

參考資料