1. 程式人生 > >似懂非懂的Comparable與Comparator

似懂非懂的Comparable與Comparator

pan sort 編譯 pre pos family 應該 可能 ret

jdk1.8.0_41

一知半解寫代碼, 集合排序用個啥。 抄起鍵盤胡亂打, 似懂非懂最可怕。

  Comparable與Comparator都是用於集合的排序,對於大多數人來說Comparator可能略微比Comparable要熟悉一點,類似下面這幾句代碼的使用頻率應該是最高的。

Collections.sort(list, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return o1.compareTo(o2);
    }
});

  這是一段對集合排序的代碼。

  對於集合排序時比較器的使用往往止步於此,以至於更為深層次的使用似懂非懂,更為復雜的排序不知所措。

  Comparable用於集合內部定義的方法實現的排序,Comparator用於集合外部實現的排序。

  我們從Collections.sort的兩個重載方法開始

public static <T extends Comparable<? super T>> void sort(List<T> list)

public static <T> void sort(List<T> list, Comparator<? super T> c)

  這兩個方法都是泛型方法,第一個方法只傳遞一個List參數進行排序,第二個方法傳遞一個List參數加上一個Comparator比較器。

public static <T extends Comparable<? super T>> void sort(List<T> list)

  Collections.sort方法用於對List集合進行排序,思考一個問題,這個只有一個參數的List如何進行排序呢?它是按照怎樣的規則進行排序呢?答案就在這個泛型方法的泛型類型之中“<T extends Comparable<? super T>>”,List集合中的元素需要實現Comparable接口,Comparable接口也是一個泛型,並要求它的泛型類型需要是集合中的元素的超類(或自身)。重點在於——集合中的元素需要是實現Comparable接口。也就是說在使用Collections.sort(List)這個方法對集合中的元素進行排序時,需要集合中的元素實現了Comparable接口,這才能進行排序。

public static <T> void sort(List<T> list, Comparator<? super T> c)

  這個方法同樣是一個泛型方法,與上面的方法不同的是對集合中的元素類型並沒有做限制,要對這個集合進行排序需要指定一個Comparator比較器,這個比較器的泛型類型需要是集合元素的超類(或自身)。

  通過上面這兩個方法比較容易的能得出一個淺顯的結論,Comparable和Comparator都是用於比較、排序,如果元素自身已經實現了Comparable接口,則可以利用它自身進行比較排序,如果元素自身沒有實現Comparable接口,則可以利用外部實現Comparator比較器對元素進行比較排序。這也就是前面提到的Comparable用於集合內部定義的方法實現的排序,Comparator用於集合外部實現的排序。 接著來看Comparable接口和Comparator接口。

  接著來看Comparable接口和Comparator接口。

Comparable

public int compareTo(T o)

  這個接口只定義了一個compareTo方法,在很多“值類型”的數據類型,例如String、Integer、Long等已經實現了這個接口。

  對於這個方法和equals方法有類似的地方,equals強調的更多是相等於否,而compareTo強調更多的比較,如果x < y,則返回-1;x = y,則返回0;x > y,則返回1。實現這個方法時同樣需要遵循幾個規則:

  1. 自反性,如果x <y ,那麽x.compareTo(y)返回-1,同理y.compareTo則返回1;
  2. 傳遞性,如果x.compareTo(y)返回-1,y.compareTo(z)返回-1,則x.compareTo(z)也應該返回-1;
  3. 同一性,如果x.compareTo(y)返回0(x = y),那麽若x.compareTo(z)返回-1時,y.compareTo(z)也應該返回-1。

  有興趣的可以查看String類中對於compareTo方法的實現,它的排序規則是將字符串轉換為字符數組逐個按照字典序排序。

Comparator

int compare(T o1, T o2)

boolean equals(Object obj)

  這個接口在JDK8中對它進行了較大的改進,在JDK8之前只包含上面兩個方法,而JDK8則達到了18個方法,其中都是接口的default方法,和static靜態方法,所以並不需要在實現時額外實現。

  在JDK8中該類添加了@FunctionalInterface函數式接口的註解,函數式接口表明在接口中只含有一個方法作為Lambda表達式的數據類型,在《JDK8的新特性——Lambda表達式》中有提到,Java中定義如果覆蓋了Object中的方法則不算,所以在Comparator接口中只有一個compare方法。對於@FunctionalInteface註解可加不可加,加上只是為了讓編譯器做更好的檢查,要求只能定義一個方法,不加編譯器便不對此進行檢查。

  compare方法和compareTo方法類似,它同樣需要滿足上面提到的:自反性、傳遞性、同一性。並且它強調,不必嚴格滿足“(compare(x, y)==0) == (x.equals(y))”,當然最好說明白。

  對於這個類,更多的是需要理解學習它所運用的設計模式——策略模式。策略模式,不改變對象自身,而是用另一個對象來改變它的行為。例如,超市減價操作,有10件商品需要統一降價1半進行處理,我們可以在這10件商品的價格上全部做修改減少至它的一半,10件好處理,如果N件呢,甚至我們還需要對其進行降價呢?此時我們則可以使用一種“策略”——商品全部打5折。這就是利用另外一個對象來改變一個對象的行為,而不是簡單粗暴地修改原有對象。說回此處,如果List中的元素本身沒有實現Comparable接口,但我們需要對它進行排序,我們可以對原有對象進行修改讓它實現Comparable接口,但凡涉及修改代碼都不優美,此時我們則可以利用策略模式,也就是實現一個Comparator接口對集合中的元素進行排序。

這是一個能給程序員加buff的公眾號

技術分享圖片

似懂非懂的Comparable與Comparator