前言

   对数据进行排序是平常经常会用到的操作之一,使用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的比较器加上链式调用可以很方便的完成逆序,多属性组合排序等排序情况,代码精简,阅读性高
  • 使用链式调用建议按照.功能分行写,便于阅读

希望这篇文章能对你有所帮助

Java8函数之旅 (五) -- Java8中的排序的更多相关文章

  1. Java8函数之旅 (二) --Java8中的流

    流与集合    众所周知,日常开发与操作中涉及到集合的操作相当频繁,而java中对于集合的操作又是相当麻烦.这里你可能就有疑问了,我感觉平常开发的时候操作集合时不麻烦呀?那下面我们从一个例子说起. 计 ...

  2. Java8函数之旅 (七) - 函数式备忘录模式优化递归

    前言 在上一篇开始Java8之旅(六) -- 使用lambda实现Java的尾递归中,我们利用了函数的懒加载机制实现了栈帧的复用,成功的实现了Java版本的尾递归,然而尾递归的使用有一个重要的条件就是 ...

  3. Java8函数之旅 (八) - 组合式异步编程

    前言 随着多核处理器的出现,如何轻松高效的进行异步编程变得愈发重要,我们看看在java8之前,使用java语言完成异步编程有哪些方案. JAVA8之前的异步编程 继承Thead类,重写run方法 实现 ...

  4. Java8函数之旅(四) --四大函数接口

    前言   Java8中函数接口有很多,大概有几十个吧,具体究竟是多少我也数不清,所以一开始看的时候感觉一脸懵逼,不过其实根本没那么复杂,毕竟不应该也没必要把一个东西设计的很复杂. 几个单词   在学习 ...

  5. Java8函数之旅 (一) 开始认识lambda

    系列之前我想说的   最近有一段时间没写博客了,这几天回到学校,才闲下来,决定写一写最近学习到的知识,既是为了分享,也是为了巩固.之前看到过一篇调查,调查说的是学习新知识,光只是看的话,知识的获取率只 ...

  6. Java8函数之旅 (三) --几道关于流的练习题

    为什么要有练习题?    所谓学而不思则罔,思而不学则殆,在系列第一篇就表明我认为写博客,既是分享,也是自己的巩固,我深信"纸上得来终觉浅,绝知此事要躬行"的道理,因此之后的几篇博 ...

  7. Java8函数之旅 (六) -- 使用lambda实现Java的尾递归

    前言 本篇介绍的不是什么新知识,而是对前面讲解的一些知识的综合运用.众所周知,递归是解决复杂问题的一个很有效的方式,也是函数式语言的核心,在一些函数式语言中,是没有迭代与while这种概念的,因为此类 ...

  8. Java8学习笔记(五)--Stream API详解[转]

    为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX 对 ...

  9. 为什么局部变量必须以final修饰(或者有final实效:java8)才可以在内部类中使用?

    为什么局部变量必须以final修饰(或者有final实效:java8)才可以在内部类中使用? public class Ace { public static void main(String[] a ...

随机推荐

  1. 利用vertical-align实现行内元素对齐

    实际项目中,常常会遇到一排行内元素对齐排列的需求,但是往往它们是这样的 我们想要的其实是这样的 曾经我一度不得不使用定位来实现我想要的位置效果,将父元素设置 position:relative ,行内 ...

  2. powerdesigner 不能自动生成注释的解决方法

    解决power designer 不能自动生成注释的解决办法只需要3步: 一.快捷键 Alt+Shift+X 打开脚本编辑器: 二.将下面天蓝色的字体脚本添加到脚本编辑器里面: Option Expl ...

  3. 解决阿里云服务器3306端口无法访问的问题(windows server 2008r2)

    3306端口一般是指mysql数据的默认端口.郁闷了几天的问题,远程无法连接服务器上的mysql服务.今天终于得到彻底解决. 首先,你要确保在服务器上安装好Mysql,并能本地启动.修改密码(如不知道 ...

  4. 【转】如何使用Git上传本地项目到github?(mac版)

    原文链接:http://www.cnblogs.com/lijiayi/p/pushtogithub.html 在此假设你已经在 github 上创建好了一个项目,像这样: 并且你已经完成了自己的项目 ...

  5. Spring Cloud Eureka Server集群Demo级搭建

    将上篇随笔Spring Cloud Eureka服务Demo级搭建进行改造,改造成一个在本机的伪集群 1.修改hosts文件(windows10 hosts文件位置:C:\Windows\System ...

  6. poj 3111 K Best 最大化平均值 二分思想

    poj 3111 K Best 最大化平均值 二分思想 题目链接: http://poj.org/problem?id=3111 思路: 挑战程序竞赛书上讲的很好,下面的解释也基本来源于此书 设定条件 ...

  7. VS2015 密钥key

    亲测可用: HMGNV-WCYXV-X7G9W-YCX63-B98R2

  8. C++ vector 常用API

    vector: 向量容器,动态数组,类模板 定义和初始化: vector<T> v1; //v1是空vector,元素类型是T类型,执行默认初始化,int为0,string为空串 vect ...

  9. poj 1035KINA Is Not Abbreviation

    题目链接:http://poj.org/problem?id=4054 本题的题意是在下面那部分待检验的单词中找到与之相对应的正确的代词,包含几种情况,一是全部字母相同,二是有一个字母不相同,三是多一 ...

  10. Map Task内部实现分析

    上篇我刚刚学习完.Spilt的过程,还算比較简单的了,接下来学习的就是Map操作的过程了,Map和Reduce一样.是整个MapReduce的重要内容,所以.这一篇,我会好好的讲讲里面的内部实现过程. ...