Java 8如何使用Lambda做排序

本篇文章將介紹使用Java 8 Lambda 進行排序。一般來說專案中要做排序,通常是由儲存資料的部分來進行,比如資料庫。但是,剛好沒有資料庫來幫忙做排序,就只能用程式的方式來進行。因為接到的專案,資料來源是JSON,而API又不排序,只能取得JSON資料後,自己來進行排序了。所以,將JSON中的資料,轉成Java的物件,在依照指定的屬性,進行排序。

先來定義Java物件:

public class Menu {
    private String name;
    private int no;

    // 其他的建構子getters/setters, equals and hashcode 就不寫了
}

不使用 Lambda 的排序方式

在Java 8 之前要做排序,可以建立一個匿名類別並且實作Comparator 介面:

new Comparator<Menu>() {
    @Override
    public int compare(Menu m1, Menu m2) {
        return m1.getName().compareTo(m2.getName());
    }
}

上面的匿名類別,依照name進行排序。

public static void main(String args[]){
    List<Menu> menus = Lists.newArrayList(
      new Menu("HomePage", 12), 
      new Menu("About US", 10)
    );
    
    Collections.sort(menus , new Comparator<Menu>() {
        @Override
        public int compare(Menu m1, Menu m2) {
            return m1.getName().compareTo(m2.getName());
        }
    });
    System.out.println(menus.get(0).getName());
}

使用 Lambda 進行簡單的排序

這邊會使用到 Java 8 新的 java.util.List 排序功能。

(final Menu m1, final Menu m2) -> m1.getName().compareTo(m2.getName());

public static void main(String args[]){
    List<Menu> menus = Lists.newArrayList(
      new Menu("HomePage", 12), 
      new Menu("About US", 10)
    );
    menus.sort((Menu m1,Menu m2) -> m1.getName().compareTo(m2.getName()));
    System.out.println(menus.get(0).getName());
}

接下來,排序的時候,不要宣告類別

這次把宣告 Menu 的部分拿掉,只留下變數名稱。

(m1, m2) -> m1.getName().compareTo(m2.getName())

public static void main(String args[]){
    List<Menu> menus = Lists.newArrayList(
      new Menu("HomePage", 12), 
      new Menu("About US", 10)
    );
    menus.sort((m1, m2) -> m1.getName().compareTo(m2.getName()));
    System.out.println(menus.get(0).getName());
}

使用自己定義的static method 進行排序

換另外一個方法,在 Menu 物件中增加一個 static mathod 如下:

public static int compareByNameAndNo(Menu m1, Menu m2) {
    if (m1.getName().equals(m2.getName())) {
        return Integer.compare(m1.getNo(), m2.getNo());
    } else {
        return m1.getName().compareTo(m2.getName());
    }
}

排序的地方,改成呼叫 static method

menus.sort(Menu::compareByNameAndNo);

如下:

public static void main(String args[]){
    List<Menu> menus = Lists.newArrayList(
      new Menu("HomePage", 12), 
      new Menu("About US", 10)
    );
    menus.sort(Menu::compareByNameAndNo);
    System.out.println(menus.get(0).getName());
}

改用元件本來就有method 進行排序(instance method)

這次我們會使用到 Comparator.comparing來進行排序。

public static void main(String args[]){
    List<Menu> menus = Lists.newArrayList(
      new Menu("HomePage", 12), 
      new Menu("About US", 10)
    );
    Collections.sort(
      menus , Comparator.comparing(Menu::getName));
    System.out.println(menus.get(0).getName());
}

反向排序

Java 8 comparator 有提供反向排序的功能。如下:

public static void main(String args[]){
    List<Menu> menus = Lists.newArrayList(
      new Menu("HomePage", 12), 
      new Menu("About US", 10)
    );
    Comparator<Menu> comparator
      = (m1, m2) -> m1.getName().compareTo(m2.getName());
    
    menus.sort(comparator.reversed());
    System.out.println(menus.get(0).getName());
}

多個不同條件進行排序

可以在排序的method中增加多個if 判斷式,比如:先進行name排序,再進行no排序。

public static void main(String args[]){
    List<Menu> menus = Lists.newArrayList(
      new Menu("HomePage", 12), 
      new Menu("About US", 10)
    );
    menus.sort((m1, m2) ->{ 
        if (m1.getName().equals(m2.getName())) {
            return Integer.compare(m1.getNo(), m2.getNo());
        } else {
            return m1.getName().compareTo(m2.getName());
        }
    });
    System.out.println(menus.get(0).getName());
}

使用 comparators 進行多個條件排序

Java 8中可以使用鏈結的方式,做到多種條件的排序,如下:

public static void main(String args[]){
    List<Menu> menus = Lists.newArrayList(
      new Menu("HomePage", 12), 
      new Menu("About US", 10)
    );
    menus.sort(
       Comparator.comparing(Menu::getName).thenComparing(Menu::getNo)
    );
    System.out.println(menus.get(0).getName());
}

使用Stream.sorted() 對 List 進行排序

我們也可以使用 Java 8 的 Stream sorted() API.來對List做排序。

有兩個選項:

  • sorted() –自然排序,前提必須要實作Comparable 介面。
  • sorted(Comparator<? super T> comparator) –針對 Comparable 的設定進行排序。
public static void main(String args[]){
    List<Menu> menus = Lists.newArrayList(
      new Menu("HomePage", 12), 
      new Menu("About US", 10)
    );
    Comparator<Menu> nameComparator = (m1, m2) -> m1.getName().compareTo(m2.getName());
    List<Menu> sortedMenus = 
    menus.stream().sorted(nameComparator).collect(Collectors.toList());
    System.out.println(sortedMenus .get(0).getName());
}

也可以使用Comparator.comparing() 來簡化程式碼。

public static void main(String args[]){
    List<Menu> menus = Lists.newArrayList(
      new Menu("HomePage", 12), 
      new Menu("About US", 10)
    );
    List<Menu> sortedMenus = menus .stream()
      .sorted(Comparator.comparing(Menu::getName))
      .collect(Collectors.toList());
    System.out.println(sortedMenus .get(0).getName());
}

使用 Stream.sorted()進行反向排序

首先,我們使用Comparator.reverseOrder()  進行反向排序。

public static void main(String args[]){
    List<String> words= Lists.newArrayList("B", "A", "C");

    List<String> reverseWords = words.stream()
      .sorted(Comparator.reverseOrder())
      .collect(Collectors.toList());
    System.out.println(reverseWords);
}

你可以在 Comparator把排序的邏輯顛倒就能達到反向排序的效果。

public static void main(String args[]){
    List<Menu> menus = Lists.newArrayList(
      new Menu("HomePage", 12), 
      new Menu("About US", 10)
    );
    Comparator<Menu> nameComparator = (m1, m2) -> m2.getName().compareTo(m1.getName());
    List<Menu> sortedMenus = 
    menus.stream().sorted(nameComparator).collect(Collectors.toList());
    System.out.println(sortedMenus .get(0).getName());
}

接著,我們來看一下怎麼使用 Comparator.comparing() 進行反向排序

public static void main(String args[]){
    List<Menu> menus = Lists.newArrayList(
      new Menu("HomePage", 12), 
      new Menu("About US", 10)
    );
    List<Menu> reverseSortedMenus = menus.stream()
      .sorted(Comparator.comparing(Menu::getName, Comparator.reverseOrder()))
      .collect(Collectors.toList());
    System.out.println(reverseSortedMenus.get(0).getName());
}

碰到Null物件怎麼辦

上面的方式,一旦碰到List當中有 null,就會發生錯誤-NullPointerException

當然,簡單的方法就是在比較之前,先針對兩個變數,判斷是否為 null。

比如:

if (m1 == null) {

return m2 == null ? 0 : 1;

} else if (m2 == null) {

return -1;

}

不過,你也可以使用 Comparator.nullsLast() 將null 擺到List的最後方,或是 nullsFirst() 將null 擺到List的最前方。

public static void main(String args[]){
    List<Menu> menus = Lists.newArrayList(
      new Menu("HomePage", 12), 
      new Menu("About US", 10)
    );
   menus .sort(Comparator.nullsFirst(Comparator.comparing(Menu::getName)));
    System.out.println(menus );
}

nullsLast(java.util.Comparator)方法返回Comparator,該Comparator是對null友好的Comparator。空值首先通過以下邏輯進行操作:

  1. null元素被認為大於非null。
  2. 當兩個元素都為空時,則認為它們相等。
  3. 當兩個元素都不為空時,指定的Comparator確定順序。
  4. 如果指定的Comparator為null,則返回的Comparator將所有非null元素視為相等。
  5. 如果指定的Comparator可序列化,則返回的Comparator可序列化。

nullsFirst(java.util.Comparator)方法返回Comparator,該Comparator是對null友好的Comparator。空值首先通過以下邏輯進行操作:

  1. null元素被認為小於non-null。
  2. 當兩個元素都為空時,則認為它們相等。
  3. 當兩個元素都不為空時,指定的Comparator確定順序。
  4. 如果指定的Comparator為null,則返回的Comparator將所有非null元素視為相等。
  5. 如果指定的Comparator可序列化,則返回的Comparator可序列化。

你也可以參考 這篇文章這篇文章

歡迎轉載本文章,請註明出處。

https://www.javainsoft.com/advanced/java-8-sort-lambda