天天看點

Comparable與Comparator淺析Comparable與Comparator淺析

Comparable與Comparator淺析

今天部落客在翻閱TreeMap的源碼,發現其鍵必須是實作Comparable或者Comparator的接口時産生了一些興趣,比如在TreeMap中的put方法分别對Comparable和Comparator接口分别進行處理。那麼疑問就來了,Comparable和Comparator接口的差別是什麼,Java中為什麼會存在兩個類似的接口?

Comparable和Comparator接口都是用來比較大小的,首先來看一下Comparable的定義:

package java.lang;
import java.util.*;
public interface Comparable<T> {
    public int compareTo(T o);
}
           

Comparator的定義如下:

package java.util;
public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}

           

Comparable對實作它的每個類的對象進行整體排序。這個接口需要類本身去實作(這句話沒看懂?沒關系,接下來看個例子就明白了)。若一個類實作了Comparable 接口,實作 Comparable 接口的類的對象的 List 清單 ( 或數組)可以通過 Collections.sort(或 Arrays.sort)進行排序。此外,實作 Comparable 接口的類的對象 可以用作 “有序映射 ( 如 TreeMap)” 中的鍵或 “有序集合 (TreeSet)” 中的元素,而不需要指定比較器。

  舉例(類Person1實作了Comparable接口)

package collections;

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

    public Person1(String name, int age)
    {
        this.name = name;
        this.age = age;
    }
    @Override
    public int compareTo(Person1 o)
    {
        return this.age-o.age;
    }
    @Override 
    public String toString()
    {
        return name+":"+age;
    }
}

           

可以看到Person1實作了Comparable接口中的compareTo方法。實作Comparable接口必須修改自身的類,即在自身類中實作接口中相應的方法。

如果我們的這個類無法修改,譬如String,我們又要對其進行排序,當然String中已經實作了Comparable接口,如果單純的用String舉例就不太形象。對類自身無法修改這就用到了Comparator這個接口(政策模式)。

如類Person2,這個類已經固定,無法進行對其類自身的修改,也修飾詞final了,你也别想繼承再implements Comparable,那麼此時怎麼辦呢?在類的外部使用Comparator的接口。如下測試代碼:

Person2 p1 = new Person2("zzh",18);
        Person2 p2 = new Person2("jj",17);
        Person2 p3 = new Person2("qq",19);
        List<Person2> list2 = new ArrayList<Person2>();
        list2.add(p1);
        list2.add(p2);
        list2.add(p3);
        System.out.println(list2);
        Collections.sort(list2,new Comparator<Person2>(){

            @Override
            public int compare(Person2 o1, Person2 o2)
            {
	            if(o1 == null || o2 == null)
					return 0;
                return o1.getAge()-o2.getAge();
            }

        });
        System.out.println(list2);

           

細心的同學可能會有疑問,明明在Comparator接口中定義了兩個方法,為什麼繼承的時候隻實作了一個方法,難道要颠覆我對Java接口常識的了解了嚒?

  實際上,我們知道當一個類沒有顯式繼承父類的時候,會有一個預設的父類,即java.lang.Object,在Object類中有一個方法即為equals方法,是以這裡并不強制要求實作Comparator接口的類要實作equals方法,直接調用父類的即可,雖然你顯式的實作了equals()方法 will be a better choice~

在《Effective Java》一書中,作者Joshua Bloch推薦大家在編寫自定義類的時候盡可能的考慮實作一下Comparable接口,一旦實作了Comparable接口,它就可以跟許多泛型算法以及依賴于改接口的集合實作進行協作。你付出很小的努力就可以獲得非常強大的功能。

  事實上,Java平台類庫中的所有值類都實作了Comparable接口。如果你正在編寫一個值類,它具有非常明顯的内在排序關系,比如按字母順序、按數值順序或者按年代順序,那你就應該堅決考慮實作這個接口。

  compareTo方法不但允許進行簡單的等同性進行比較,而且語序執行順序比較,除此之外,它與Object的equals方法具有相似的特征,它還是一個泛型。類實作了Comparable接口,就表明它的執行個體具有内在的排序關系,為實作Comparable接口的對象數組進行排序就這麼簡單: Arrays.sort(a);

對存儲在集合中的Comparable對象進行搜尋、計算極限值以及自動維護也同樣簡單。列如,下面的程式依賴于String實作了Comparable接口,它去掉了指令行參數清單中的重複參數,并按字母順序列印出來:

public class WordList{
    public static void main(String args[]){
        Set<String> s = new TreeSet<String>();
        Collections.addAll(s,args);
        System.out.println(s);
    }
}
           

Comparable 是排序接口;若一個類實作了 Comparable 接口,就意味着 “該類支援排序”。而 Comparator 是比較器;我們若需要控制某個類的次序,可以建立一個 “該類的比較器” 來進行排序。

  前者應該比較固定,和一個具體類相綁定,而後者比較靈活,它可以被用于各個需要比較功能的類使用。可以說前者屬于 “靜态綁定”,而後者可以 “動态綁定”。

  我們不難發現:Comparable 相當于 “内部比較器”,而 Comparator 相當于 “外部比較器”。

繼續閱讀