概述
Comparable在java.lang包下,閱讀原始碼後發現其內部只有一個方法compareTo()
。從詞根上分析,Comparable以-able結尾,表示它有自身具備的某種能力的性質,表明了Comparable物件本身是可以與同型別進行比較的,其比較方法為compareTo。
public interface Comparable<T> {
public int compareTo(T o);
}
複製程式碼
關注點
- Comparable介面會對實現它的類施加(impose)一個整體的順序。這一順序被認為是類的自然排序,類的compareTo方法被認為是它的自然比較方法。
- 實現此介面的物件列表(和陣列)可以通過
Collections.sort
(和Arrays.sort
)進行自動排序。實現這一介面的物件在有序Map中,有序是按照key進行排序的;在有序Set中,是按照set集合中的元素排序的。而使用這些方法時,我們並不需要指定比較器comparator(說明:這些排序都是預設升序排序,且排序欄位只有一個。如果一個類有多個排序欄位,要對這個類集合進行排序,則需要重寫比較器方法)。 - 對於類C的任意變數e1和e2,當且僅當
e1.compareTo(e2) == 0
與e1.equals(e2)
具有相同的boolean值時,類C的自然排序才叫做與equals一致。注意,null
不是任何類的例項,即使e.equals(null)
返回false
,e.compareTo(null)
也將丟擲NullPointerException
。 - 我們強烈建議(儘管並不是必須的):自然排序應該和equals結果保持一致。是因為自然排序用到了compareTo方法,這裡的意思是需要滿足關係:
e1.compareTo(e2) == 0
的和1.equals(e2)
有相同的返回。這是因為沒有明確比較器的有序set(和有序map),如果自然排序不能和equals方法保持一致,那麼它們會表現出一些詭異的行為。而且,這樣的有序set(或者map)和equals中通用規範是矛盾的。 - 舉個例子:如果向一個沒有明確比較器的有序set中新增2個值a和b。
a.equals(b)
值為false
,而a.compareTo(b) == 0
值為true
,那麼第二次的add操作會失敗。因為從有序set的角度看,a和b是等值的。(出現這種事情就很詭異了,明明a在add之後,b再add時,這是兩個不同的值,應該被正常新增到集合中,但是卻被拒絕了,因為add時,使用到了方法compareTo,去比較插入的值是否存在,而根據返回結果為0,這樣二者就被認為是相同的值。所以我們一再強調:為避免這種異常,自然排序要和equals結果保持一致,必須滿足e1.compareTo(e2) == 0
的和e1.equals(e2)
始終有相同的返回值) - 實質上,所有實現了Comparable介面的java核心類,都滿足自然排序的要求。唯一的例外類是:BigDecimal類。它的自然排序要求是:值相等而精度是不等的。所以,精度不同但值相同的兩個BigDecimal物件,它們的equals方法返回值應該為true,而compareTo()方法應該返回0:
public static void main(String[] args){
BigDecimal a = new BigDecimal(2.30);
BigDecimal b = new BigDecimal(2.3);
System.out.println("a.eauals(b): " + a.equals(b));
System.out.println("a.compareTo(b): " + a.compareTo(b));
}
----------------outpout----------------
a.eauals(b): true
a.compareTo(b): 0
複製程式碼
方法
Comparable介面唯一的方法定義如下:
public int compareTo(T o);
複製程式碼
對於a.compareTo(b)
返回值定義如下:
- a > b => 整數
- a == b => 0
- a < b => 負數
異常情況:
- NullPointException: 入參為null
- ClassCastException: 入參型別與被比較型別不相容,如執行
new Integer(1).compareTo(new Double(2.1));
時,編譯錯誤:Error:(35, 23) java: 不相容的型別: java.lang.Double無法轉換為java.lang.Integer
看下Integer類實現的compareTo方法和equals方法:
public final class Integer extends Number implements Comparable<Integer> { // 實現Comparable介面時,加上泛型限定,實現編譯階段入參型別檢查
...
@Override
public int compareTo(Integer anotherInteger){
return (this.vaule < anotherInteger.value) ? -1
: ((this.value == anotherInteger.value) ? 0 : 1);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
...
}
複製程式碼