前言

   对数据进行排序是平常经常会用到的操作之一,使用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. 企业级Tomcat部署实践及安全调优

    1.1 Tomcat简介 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache.Sun和其他一些公司及个人 ...

  2. SurfaceView 使用demo 飞机游戏小样

    本demo 主要使用了surfaceview 画图. 1.在线程中对canvas操作. 2.实现画图 3.surfaceView 继承了view 可以重写ontouchevent方法来操作输入. 代码 ...

  3. SQL---约束---add constraint方法添加约束

    1.主键约束: 格式为:alter table 表格名称 add constraint 约束名称 增加的约束类型 (列名) 例子:alter table emp add constraint ppp ...

  4. python利用scrapy框架爬取起点

    先上自己做完之后回顾细节和思路的东西,之后代码一起上. 1.Mongodb 建立一个叫QiDian的库,然后建立了一个叫Novelclass(小说类别表)Novelclass(可以把一级类别二级类别都 ...

  5. Unity3d_GUI_2__(能量条的学习)

    这和上一篇有点跳跃,不过GUI有官方文档,而且也可以查看编辑器自带的脚本文档,多看看API,多操作操作,肯定能熟练的.所以这篇我就介绍下一点小技巧,大佬就略过这篇了,不适合大佬,会被你们教育的. 1. ...

  6. ctags-vim代码间快速跳转

    ctags-vim代码间快速跳转 1.说明 在Linux环境下使用vim进行驱动程序编写和维护的时候,会经常需要调用Linux内核的函数或宏定义,在驱动程序和kernel代码之间频繁跳转是件很繁琐的事 ...

  7. 谈谈微服务中的 API 网关(API Gateway)

    前言 又是很久没写博客了,最近一段时间换了新工作,比较忙,所以没有抽出来太多的时间写给关注我的粉丝写一些干货了,就有人问我怎么最近没有更新博客了,在这里给大家抱歉. 那么,在本篇文章中,我们就一起来探 ...

  8. RabbitMQ Cluster群集安装配置

    #RabbitMQ Cluster群集安装配置 openstack pike 部署 目录汇总 http://www.cnblogs.com/elvi/p/7613861.html ########## ...

  9. Linux多线程编程详细解析----条件变量 pthread_cond_t

    Linux操作系统下的多线程编程详细解析----条件变量 1.初始化条件变量pthread_cond_init #include <pthread.h> int pthread_cond_ ...

  10. 移动GIS在企业各个行业中的应用解决方案

    “移动GIS的设备厂商越来越多地关注行业用户的需求,所以移动GIS的市场前景是非常广阔的.当前国内移动GIS,已广泛应用于测绘.国土.环境.水利.农业.林业和矿产等传统资源管理领域和城市规划方面.在应 ...