Java8函数之旅 (五) -- Java8中的排序

前言

   对数据进行排序是日常常常会用到的操做之一,使用Jav8排序能够减小你在排序这方面的代码量,优化你的代码。html

测试用例代码

定义个实体类User,拥有姓名name,年龄age,积分credits三个属性,定义一个包含User的集合,用于排序,下面是代码java

/* 这里偷个懒,用lombok注解生成实体类getset等一些基本方法 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String name;
    private Integer age;
    private Integer credits;
}

初始化待排序的集合app

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的匿名内部类实现ide

@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方法要好的。code

对年龄从大到小排序(反向排序)

Before Java8

一样是经过匿名内部类这是此次将 o1 - o2 的顺序调换一下htm

@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

和匿名内部类的颠倒同样,只是这里有之间的类库反转blog

@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的比较器加上链式调用能够很方便的完成逆序,多属性组合排序等排序状况,代码精简,阅读性高
  • 使用链式调用建议按照.功能分行写,便于阅读

但愿这篇文章能对你有所帮助 :)

上一篇:开始Java8之旅(四) --四大函数接口
下一篇:开始Java8之旅(六) --使用lambda实现Java的尾递归

相关文章
相关标签/搜索