前言

   对数据进行排序是平常经常会用到的操作之一,使用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. 流式处理的新贵 Kafka Stream - Kafka设计解析(七)

    原创文章,转载请务必将下面这段话置于文章开头处. 本文转发自技术世界,原文链接 http://www.jasongj.com/kafka/kafka_stream/ Kafka Stream背景 Ka ...

  2. Java提高十七:TreeSet 深入分析

    前一篇我们分析了TreeMap,接下来我们分析TreeSet,比较有意思的地方是,似乎有Map和Set的地方,Set几乎都成了Map的一个马甲.此话怎讲呢?在前面一篇讨论HashMap和HashSet ...

  3. 关于控制台输出 警告 log4j:WARN No appenders could be found for logger

    新建struts2项目时出现警告 log4j:WARN No appenders could be found for logger 于是上网搜查了解决方案 转自:最爱NBA 在src下面新建file ...

  4. 新一代 CI 持续集成工具 flow.ci 正式开源

    很高兴地宣布 flow.ci 在 Apache-2.0 协议下正式开源了.flow.ci 是国内首套开源持续集成(CI) 解决方案,帮助企业团队实现开发流程(build-test-deploy)自动化 ...

  5. Linux 链接详解(2)

    可执行文件加载执行过程: 上一节我们说到ELF文件格式,静态库的符号解析和重定位的内容.这一节我们来分析一下可执行文件. 由上一节我们知道可执行文件也是ELF文件,当程序被加载器加载到内存时是按照EL ...

  6. 51nod 1058 N的阶乘的长度 位数公式

    1058 N的阶乘的长度基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注输入N求N的阶乘的10进制表示的长度.例如6! = 720,长度为3.Input输入N( ...

  7. codeforces 887B Cubes for Masha 两种暴力

    B. Cubes for Masha time limit per test 1 second memory limit per test 256 megabytes input standard i ...

  8. Mysql 表分区和性能

    以下内容节选自<Mysql技术内幕InnoDB存储引擎> mysql表分区: 分区功能并不是所有存储引擎都支持的,如CSV.MERGE等就不支持.mysql数据库支持的分区类型为水平分区( ...

  9. Python爬虫(十五)_案例:使用bs4的爬虫

    本章将从Python案例讲起:所使用bs4做一个简单的爬虫案例,更多内容请参考:Python学习指南 案例:使用BeautifulSoup的爬虫 我们已腾讯社招页面来做演示:http://hr.ten ...

  10. RecyclerView.Adapter优化了吗?

    昨天写了一篇「还在用ListView?」讲的内容是RecyclerView的使用技巧以及一些经常使用的开源库.有朋友反馈"我已经在用recyclerview了",那么怎样让它更好用 ...