java8——排序

 對數據進行排序是平常經常會用到的操作之一,使用Jav8排序可以減少你在排序這方面的代碼量,優化你的代碼。

測試用例代碼

定義個實體類User,擁有姓名name,年齡age,積分credits三個屬性,定義一個包含User的集合,用於排序,下面是代碼

/* 這裏偷個懶,用lombok註解生成實體類getset等一些基本方法 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String name;
    private Integer age;
    private Integer credits;
}

初始化待排序的集合

    private List<User> users = Lists.newArrayList(
            new User("jack",17,10),
            new User("jack",18,10),
            new User("jack",19,11),
            new User("apple",25,15),
            new User("tommy",23,8),
            new User("jessica",15,13)
            );

排序

對年齡從小到大排序

Before Java8

根據User年齡從小到大排序,使用Collections.sort方法,通過Comparator的匿名內部類實現

    @Test
    public void traditionCompareByName(){
        Collections.sort(users, new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return o1.getAge() - o2.getAge();
            }
        });

        for (User user : users) {
            System.out.println(user);
        }
    }

結果

User(name=jessica, age=15, credits=13)
User(name=jack, age=17, credits=10)
User(name=jack, age=18, credits=10)
User(name=jack, age=19, credits=11)
User(name=tommy, age=23, credits=8)
User(name=apple, age=25, credits=15)

Process finished with exit code 0

in Java8

這裏使用lambda表達式來代替匿名內部類,並且使用list接口下的sort方法(java8新增加),再鏈式輸出

    @Test
    public void traditionCompareByNameInJava8(){
        users.sort((o1, o2) -> o1.getAge() - o2.getAge());
        users.forEach(user -> System.out.println(user));
    }

輸出結果就不再顯示了
當然還可以通過方法引用進一步的簡化,這裏使用Comparator下的comparingInt進行排序,使用User::getAge獲得年齡,默認從小到大正向排序

import static java.util.Comparator.comparingInt;

    @Test
    public void traditionCompareByNameInJava8(){
        users.sort(comparingInt(User::getAge));
        users.forEach(System.out::println);
    }

對比

簡單對比一下,可以發現使用Java8的排序對於簡單的排序無論是從代碼量還是可以閱都是比之前的匿名內部類實現compare方法要好的。

對年齡從大到小排序(反向排序)

Before Java8

同樣是通過匿名內部類這是這次將 o1 - o2 的順序調換一下

    @Test
    public void traditionCompareByNameReverse(){
        Collections.sort(users, new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return o2.getAge() - o1.getAge();
            }
        });
    }

In Java8

和匿名內部類的顛倒一樣,只是這裏有之間的類庫反轉

    @Test
    public void traditionCompareByNameInJava8Reverse(){
        users.sort((o1, o2) -> o1.getAge() - o2.getAge());
    }

在比較器後面增加reversed即可,鏈式調用是java8的風格之一

    @Test
    public void traditionCompareByNameInJava8Reverse(){
        users.sort(comparingInt(User::getAge).reversed());
    }

同樣是閱讀性,原先的匿名內部類方法不僅閱讀困難,一個簡單的倒序也需要先去觀察o2-o1還是o1-o2才能得出,而Java8的方法不僅代碼簡潔,可讀性還很高,compare getAge讀出是通過年齡進行排序,reversed讀出是倒序。

根據姓名,年齡,積分排序

按照姓名,年齡與積分的順序依次排序,也就是多條件組合排序

Before Java8

讓我們看看傳統的方式該如何實現

    @Test
    public void traditionCombinationCompare(){
        Collections.sort(users, new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                if (o1.getName().equals(o2.getName())) {
                    if (o1.getAge().equals(o2.getAge())) {
                        return o1.getAge() - o2.getAge();
                    } else {
                        return o1.getCredits() - o2.getCredits();
                    }
                } else {
                    return o1.getName().compareTo(o2.getName());
                }
            }
        });
    }

這樣的代碼我相信誰都不太想看,我自己寫完都需要驗證一遍以保證真的沒有哪裏邏輯寫錯,這樣的做法不僅效率底下,還容易犯錯,這種代碼更是他人的噩夢。

in Java8

  • 在這裏我們使用比較器的thenComparing實現鏈式調用
    @Test
    public void traditionCombinationCompareInJava8(){
        users.sort(comparing(User::getName)
                .thenComparing(User::getAge)
                .thenComparing(User::getCredits));
    }

可讀性也很好,這樣的代碼幾乎連註釋都省去了,很清晰的可以看出排序的順序,修改起來也很容易,而上面的代碼如果要修改成另外一種次序,整個嵌套邏輯結構條件都要改動。

  • 另外如果需求變成如下,按照姓名順序->年齡倒序->積分順序的次序來排序,Java8也十分容易,comparing比較器提供了重載方法,可以自定義某條屬性的排序,例子如下
    @Test
    public void traditionCombinationCompareInJava8(){
        users.sort(comparing(User::getName)
                .thenComparing(User::getAge, (o1, o2) -> o2 - o1)
                .thenComparing(User::getCredits));
    }
  • update(10-24)
    事實上 o2 - o1 這樣的代碼還是有一些命令式的風格,即包含了具體的實現過程(o2 -o1這樣的代碼),thenComparaing方法可以直接接受一個排序器,因此我們只要直接將倒序的排序器當做參數傳入即可,代碼如下
    @Test
    public void traditionCombinationCompareInJava8(){
        users.sort(comparing(User::getName)
                .thenComparing(comparing(User::getAge).reversed())
                .thenComparing(User::getCredits));
        users.forEach(System.out::println);
    }

很清晰的可以看到第二行的getAge是倒序,而其他的屬性依舊是正序,建議大家使用鏈式寫法的時候像上面一樣分行,提高可讀性

總結

  • 使用lambda表達式可以代替傳統的匿名內部類,精簡代碼量,提高可讀性,可以進一步使用方法引用繼續精簡
  • 使用Comparing的比較器加上鍊式調用可以很方便的完成逆序,多屬性組合排序等排序情況,代碼精簡,閱讀性高
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章