Comparable 和 Comparator 的區別

Java 中爲咱們提供了兩種比較機制:Comparable 和 Comparator,他們之間有什麼區別呢?今天來了解一下。java

Comparable 天然排序數組

Comparable 在 java.lang 包下,是一個接口,內部只有一個方法 compareTo():網絡

public interface Comparable<T> {
    public int compareTo(T o);
}

Comparable 可讓實現它的類的對象進行比較,具體的比較規則是按照 compareTo 方法中的規則進行。這種順序稱爲 天然順序。框架

compareTo 方法的返回值有三種狀況:ide

e1.compareTo(e2) > 0 即 e1 > e2 e1.compareTo(e2) = 0 即 e1 = e2 e1.compareTo(e2) < 0 即 e1 < e2 注意:測試

1.因爲 null 不是一個類,也不是一個對象,所以在重寫 compareTo 方法時應該注意 e.compareTo(null) 的狀況,即便 e.equals(null) 返回 false,compareTo 方法也應該主動拋出一個空指針異常 NullPointerException。this

2.Comparable 實現類重寫 compareTo 方法時通常要求 e1.compareTo(e2) == 0 的結果要和 e1.equals(e2) 一致。這樣未來使用 SortedSet 等根據類的天然排序進行排序的集合容器時能夠保證保存的數據的順序和想象中一致。設計

有人可能好奇上面的第二點若是違反了會怎樣呢?指針

舉個例子,若是你往一個 SortedSet 中前後添加兩個對象 a 和 b,a b 知足 (!a.equals(b) && a.compareTo(b) == 0),同時也沒有另外指定個 Comparator,那當你添加完 a 再添加 b 時會添加失敗返回 false, SortedSet 的 size 也不會增長,由於在 SortedSet 看來它們是相同的,而 SortedSet 中是不容許重複的。code

實際上全部實現了 Comparable 接口的 Java 核心類的結果都和 equlas 方法保持一致。 實現了 Comparable 接口的 List 或則數組可使用 Collections.sort() 或者 Arrays.sort() 方法進行排序。

實現了 Comparable 接口的對象纔可以直接被用做 SortedMap (SortedSet) 的 key,要否則得在外邊指定 Comparator 排序規則。

所以本身定義的類若是想要使用有序的集合類,須要實現 Comparable 接口,好比:

**
 * description: 測試用的實體類 書, 實現了 Comparable 接口,天然排序
 * 
 * author: shixinzhang
 * 
 * data: 10/5/2016
 */
public class BookBean implements Serializable, Comparable {
    private String name;
    private int count;
 
    public BookBean(String name, int count) {
        this.name = name;
        this.count = count;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getCount() {
        return count;
    }
 
    public void setCount(int count) {
        this.count = count;
    }
 
    /**
     * 重寫 equals
     * @param o
     * @return
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof BookBean)) return false;
 
        BookBean bean = (BookBean) o;
 
        if (getCount() != bean.getCount()) return false;
        return getName().equals(bean.getName());
 
    }
 
    /**
     * 重寫 hashCode 的計算方法
     * 根據全部屬性進行 迭代計算,避免重複
     * 計算 hashCode 時 計算因子 31 見得不少,是一個質數,不能再被除
     * @return
     */
    @Override
    public int hashCode() {
        //調用 String 的 hashCode(), 惟一表示一個字符串內容
        int result = getName().hashCode();
        //乘以 31, 再加上 count
        result = 31 * result + getCount();
        return result;
    }
 
    @Override
    public String toString() {
        return "BookBean{" +
                "name='" + name + '\'' +
                ", count=" + count +
                '}';
    }
 
    /**
     * 當向 TreeSet 中添加 BookBean 時,會調用這個方法進行排序
     * @param another
     * @return
     */
    @Override
    public int compareTo(Object another) {
        if (another instanceof BookBean){
            BookBean anotherBook = (BookBean) another;
            int result;
 
            //好比這裏按照書價排序
            result = getCount() - anotherBook.getCount();     
 
          //或者按照 String 的比較順序
          //result = getName().compareTo(anotherBook.getName());
 
            if (result == 0){   //當書價一致時,再對比書名。 保證全部屬性比較一遍
                result = getName().compareTo(anotherBook.getName());
            }
            return result;
        }
        // 同樣就返回 0
        return 0;
    }

上述代碼還重寫了 equlas(), hashCode() 方法,自定義的類未來可能會進行比較時,建議重寫這些方法。

感謝 @li1019865596 指出,這裏我想表達的是在有些場景下 equals 和 compareTo 結果要保持一致,這時候不重寫 equals,使用 Object.equals 方法獲得的結果會有問題,好比說 HashMap.put() 方法,會先調用 key 的 equals 方法進行比較,而後才調用 compareTo。

後面重寫 compareTo 時,要判斷某個相同時對比下一個屬性,把全部屬性都比較一次。

Comparable 接口屬於 Java 集合框架的一部分。

Comparator 定製排序

Comparator 在 java.util 包下,也是一個接口,JDK 1.8 之前只有兩個方法:

public interface Comparator<T> {
 
    public int compare(T lhs, T rhs);
 
    public boolean equals(Object object);
}

JDK 1.8 之後又新增了不少方法:

輸入圖片說明

基本上都是跟 Function 相關的,這裏暫不介紹 1.8 新增的。

從上面內容可知使用天然排序須要類實現 Comparable,而且在內部重寫 comparaTo 方法。

而 Comparator 則是在外部制定排序規則,而後做爲排序策略參數傳遞給某些類,好比 Collections.sort(), Arrays.sort(), 或者一些內部有序的集合(好比 SortedSet,SortedMap 等)。

使用方式主要分三步:

建立一個 Comparator 接口的實現類,並賦值給一個對象 在 compare 方法中針對自定義類寫排序規則 將 Comparator 對象做爲參數傳遞給 排序類的某個方法 向排序類中添加 compare 方法中使用的自定義類 舉個例子:

// 1.建立一個實現 Comparator 接口的對象
Comparator comparator = new Comparator() {
    @Override
    public int compare(Object object1, Object object2) {
        if (object1 instanceof NewBookBean && object2 instanceof NewBookBean){
            NewBookBean newBookBean = (NewBookBean) object1;
            NewBookBean newBookBean1 = (NewBookBean) object2;
            //具體比較方法參照 天然排序的 compareTo 方法,這裏只舉個栗子
            return newBookBean.getCount() - newBookBean1.getCount();
        }
        return 0;
    }
};
//2.將此對象做爲形參傳遞給 TreeSet 的構造器中
TreeSet treeSet = new TreeSet(comparator);
 
//3.向 TreeSet 中添加 步驟 1 中 compare 方法中設計的類的對象
treeSet.add(new NewBookBean("A",34));
treeSet.add(new NewBookBean("S",1));
treeSet.add( new NewBookBean("V",46));
treeSet.add( new NewBookBean("Q",26));

其實能夠看到,Comparator 的使用是一種策略模式,不熟悉策略模式的同窗能夠點這裏查看: 策略模式:網絡小說的固定套路 瞭解。

排序類中持有一個 Comparator 接口的引用:

Comparator<? super K> comparator; 而咱們能夠傳入各類自定義排序規則的 Comparator 實現類,對一樣的類制定不一樣的排序策略。

總結

Java 中的兩種排序方式:

Comparable 天然排序。(實體類實現) Comparator 是定製排序。(沒法修改實體類時,直接在調用方建立) 同時存在時採用 Comparator(定製排序)的規則進行比較。

**對於一些普通的數據類型(好比 String, Integer, Double…),它們默認實現了Comparable 接口,實現了 compareTo 方法,咱們能夠直接使用。

而對於一些自定義類,它們可能在不一樣狀況下須要實現不一樣的比較策略,咱們能夠新建立 Comparator 接口,而後使用特定的 Comparator 實現進行比較。 **

這就是 Comparable 和 Comparator 的區別。

相關文章
相關標籤/搜索