Java利用Comparator實現分組排序

Ease發表於2019-02-15

在資料庫中我們可以使用 order bygroup by 輕鬆實現分組和排序的功能,那麼在Java中我們又該如何實現呢?下面我們一起來研究一番

ComparatorComparable

  • Comparable 是一個排序介面,實現了該介面的類,表示該類支援排序功能,重寫 compareTo 方法可使程式按照我們的意願對陣列或列表進行排序

  • Comparator 是一個比較器介面,如果我們需要對類進行排序,而該類並沒有實現 Comparable 介面,那麼我們可以通過實現 Comparator 介面來保持類的排序能力

簡單排序

  1. 建立一個 Bean 類,我們將對該類進行排序

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Student {
    
       /**
       * 班級名稱
       */
       private String className;
    
       /**
       * 學生姓名
       */
       private String name;
    
       /**
       * 分數
       */
       private Integer score;
    }
    複製程式碼
  2. 準備一些資料,用於測試

    final String classOne = "一班";
    final String classTwo = "二班";
    final String classThree = "三班";
    List<Student> students = new ArrayList<>();
    students.add(new Student(classOne, "李一", 43));
    students.add(new Student(classOne, "王二", 75));
    students.add(new Student(classOne, "張三", 91));
    students.add(new Student(classTwo, "郭子", 59));
    students.add(new Student(classTwo, "潘子", 88));
    students.add(new Student(classTwo, "劉子", 97));
    students.add(new Student(classThree, "陳六", 66));
    students.add(new Student(classThree, "唐七", 77));
    students.add(new Student(classThree, "周八", 89));
    複製程式碼
  3. 建立一個 Comparator 物件

    如果我們將第一引數與第二個引數進行比較,那麼表示這是一個升序序列(自然排序),否則表示一個降序序列。 當然這樣說並不完全準確,主要還是看返回的整數值是大於零、小於零還是等於零,只有當大於零時物件 o2 才會置於物件 o1 前面

    // 按分數進行降序排序
    Comparator<Student> comparator = (o1, o2) -> o2.getScore().compareTo(o1.getScore());
    複製程式碼
  4. 測試排序結果

    Collections.sort(students, comparator);
    students.forEach(System.out::println);
    複製程式碼

    列印結果,可以看出列表是按分數進行排序的,接下來我們將對班級名稱進行分組並按分數排序

    Student(className=二班, name=劉子, score=97)
    Student(className=一班, name=張三, score=91)
    Student(className=三班, name=周八, score=89)
    Student(className=二班, name=潘子, score=88)
    Student(className=三班, name=唐七, score=77)
    Student(className=一班, name=王二, score=75)
    Student(className=三班, name=陳六, score=66)
    Student(className=二班, name=郭子, score=59)
    Student(className=一班, name=李一, score=43)
    複製程式碼

分組排序

  1. 建立一個分組排序的 Comparator 物件

    由於我們需要按班級名稱進行分組排序,所以我們必須進行兩次計較,第一次比較是否是相同的班級名稱,如果不是直接返回比較的結果值即可,否則返回分數的比較結果

    Comparator<Student> groupComparator = (o1, o2) -> {
        int diff = o1.getClassName().compareTo(o2.getClassName());
        return diff == 0 ? o2.getScore().compareTo(o1.getScore()) : diff;
    };
    複製程式碼
  2. 測試排序結果

    Collections.sort(students, groupComparator);
    students.forEach(System.out::println);
    複製程式碼

    列印結果,可以看出確實時按班級分組排序的,但是如果我們想要班級名稱也是按順序排的呢,怎麼做?

    Student(className=一班, name=張三, score=91)
    Student(className=一班, name=王二, score=75)
    Student(className=一班, name=李一, score=43)
    Student(className=三班, name=周八, score=89)
    Student(className=三班, name=唐七, score=77)
    Student(className=三班, name=陳六, score=66)
    Student(className=二班, name=劉子, score=97)
    Student(className=二班, name=潘子, score=88)
    Student(className=二班, name=郭子, score=59)
    複製程式碼

有序的分組排序

如何讓分組的名稱按照我們想要的順序出現?簡單,請看下面

  1. 定義一個分組順序

    分組順序用來判斷該分組所在位置,這樣我們就可以自定義分組的位置,從而實現有序的分組

    Map<String, Integer> groupOrder = new HashMap<>(4);
    groupOrder.put(classOne, 1);
    groupOrder.put(classTwo, 2);
    groupOrder.put(classThree, 3);
    複製程式碼
  2. 建立一個有序的分組排序 Comparator 物件

    通過自定義的班級順序來決定班級名稱出現的位置

    Comparator<Student> orderlyGroupComparator = (o1, o2) -> {
        int diff = groupOrder.get(o1.getClassName()).compareTo(groupOrder.get(o2.getClassName()));
        return diff == 0 ? o2.getScore().compareTo(o1.getScore()) : diff;
    };
    複製程式碼
  3. 測試排序結果

    Collections.sort(students, orderlyGroupComparator);
    students.forEach(System.out::println);
    複製程式碼

    列印結果

    Student(className=一班, name=張三, score=91)
    Student(className=一班, name=王二, score=75)
    Student(className=一班, name=李一, score=43)
    Student(className=二班, name=劉子, score=97)
    Student(className=二班, name=潘子, score=88)
    Student(className=二班, name=郭子, score=59)
    Student(className=三班, name=周八, score=89)
    Student(className=三班, name=唐七, score=77)
    Student(className=三班, name=陳六, score=66)
    複製程式碼

總結

現在回過去看,原來分組排序如此的簡單呀,不過這完全得益於 Comparator 的強大,你也完全可以選擇實現 Comparable 介面來達到相同的效果。

當有新的挑戰時,我們一定要多思考:facepunch:

本示例原始碼參考

由於本人水平有限,有不正確的地方,還望指正,謝謝

相關文章