Java 集合排序策略介面 Comparator

碼農小胖哥發表於2020-05-20

1. 前言

最近用到了集合排序(基於 Java 8)。現在我能用 Stream 的就用 Stream ,真香!排序可以這麼寫:

List<People> peoples = new ArrayList<>();
  // 中間省略
  // 按照年齡從小到大排序
peoples.sort(Comparator.comparing(People::getAge));

這裡排序用到了一個關鍵介面 java.util.Comparator。排序比較作為業務中經常出現的需求,我們有必要研究一下這個介面。

2. Comparator 概念

Comparator 是一個函式式介面。它經常用於沒有天然排序的集合進行排序,如 Collections.sortArrays.sort。或者對於某些有序資料結構的排序規則進行宣告,如 TreeSetTreeMap 。也就是該介面主要用來進行集合排序。

3. Comparator 中的方法

Comparator 作為一個函式式介面只有一個抽象方法,但是它有很多的預設方法,我們來認識一下這些方法們。

3.1 compare 抽象方法

作為Comparator 唯一的抽象方法,int compare(T o1,T o2) 比較兩個引數的大小, 返回負整數,零,正整數 ,分別代表 o1<o2o1=o2o1>o2,通常分別返回 -101。偽表示式:

// 輸入兩個同型別的物件 ,輸出一個比較結果的int數字
(x1,x2)-> int

實現該方法一定要注意以下事項:

  • 必須保證compare(x,y)compare(y,x) 的值的和必須為 0
  • 必須保證比較的順序關係是可傳遞的,如果compare(x,y)>0 而且compare(y,z)>0compare(x,z)>0
  • 如果存在 compare(x,y)=0,則對於 z 而言,存在 compare(x, z)==compare(y, z)

然而並不 嚴格要求(compare(x, y)==0) == (x.equals(y))。一般說來,任何違背這個條件的 Comparator 實現都應該明確指出這一事實情況。

3.2 comparing 系列方法

Java 8 開始,Comparator 提供了一系列的靜態方法,並通過函式式的風格賦予 Comparator 更加強大和方便的功能,我們暫且稱它們為 comparing系列方法。

   public static <T, U> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor,
            Comparator<? super U> keyComparator)
    {
        Objects.requireNonNull(keyExtractor);
        Objects.requireNonNull(keyComparator);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
                                              keyExtractor.apply(c2));
    }

該方法是該系列方法的基本方法。是不是看上去很難懂的樣子?我們來分析一下該方法。它一共兩個引數都是函式式介面。

第一個引數 Function<? super T, ? extends U> keyExtractor 表示輸入一個是 T 型別物件,輸出一個 U 型別的物件,舉個例子,輸入一個 People 物件返回其年齡 Integer 數值:

//   people -> people.getAge(); 轉換為下面方法引用
Function<People, Integer> getAge = People::getAge;

第二個引數 keyComparator就很好理解了,表示使用的比較規則。

c1,c2 按照 第一個引數 keyExtractor 提供的規則進行提取特徵,然後第二個引數keyComparator對這兩個特徵進行比較。下面的式子其實可以概括為 3.1(x1,x2)-> int

(c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
                                              keyExtractor.apply(c2))

Comparator & Serializable 為 Java 8 新特性:同時滿足這兩個型別約束

理解了這個方法後,其它該系列的方法就好理解了,這裡不再贅述。目前 comparing 系列方法使用更加廣泛。我們舉一些例子:

List<People> peoples = new ArrayList<>();
//  ………………
// 按照年齡從低到高排序
peoples.sort(Comparator.comparing(People::getAge));
// 按照年齡從高到低排序
peoples.sort(Comparator.comparing(People::getAge, (x, y) -> -x.compareTo(y)));

同樣你可以使用 java.util.Collections 或者 Stream 提供的排序方法來使用Comparator

4. 小結

今天對 Comparator進行了簡單的分析,它用於構建集合排序的規則,在日常開發中非常有用。下一篇 我們將對另一個和它十分相似的介面 Comparable 進行分析和比較它們的不同,敬請關注。

關注公眾號:Felordcn 獲取更多資訊

個人部落格:https://felord.cn

相關文章