前言

   对数据进行排序是平常经常会用到的操作之一,使用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. laravel whereDate()方法的使用

    今天在做日期查询,过过滤的时候,发现了一个问题,用where()语句,要求时间格式比较严格,比如你要查2017-09-23这一天的活动,你在输入框传入2017-09-23,系统会默认帮你补上后面缺少的 ...

  2. postman接口测试系列:接口参数化和参数的传递

    接着上一个章节时间戳和加密继续,上一节中我们使用Pre-Request Script可以正确获取时间戳和加密后的数据,接口响应结果也达到了预期目标.这里先简单说明一下接口的用例设计的测试点,截图所示 ...

  3. ASP.NET没有魔法——ASP.NET Identity 的“多重”身份验证代码篇

    上篇文章介绍了ASP.NET中身份验证的机制与流程,本文将使用代码的来介绍如何实现第三方账户验证与双因子验证. 本章主要内容有: ● 实现基于微软账户的第三方身份验证 ● 实现双因子身份验证 ● 验证 ...

  4. C语言之阶乘

    #include<stdio.h>#include<stdlib.h>#include<time.h>int main(){ int num,i,result=1; ...

  5. Linux运维项目实战系列

    Linux运维项目实战系列 项目实战1-LNMP的搭建.nginx的ssl加密.权限控制的实现 项目实战2-项目实战2-实现基于LVS负载均衡集群的电商网站架构 2.1项目实战2.1-nginx 反向 ...

  6. FastDFS教程Ⅲ-文件服务器扩容

    1.简介     FastDFS文件服务器在设计时,为了支持大容量,存储节点(服务器)采用了分卷(或分组)的组织方式.存储系统由一个或多个卷组成,卷与卷之间的文件是相互独立的,所有卷的文件容量累加就是 ...

  7. js屏蔽广告

    最近遇到有些广告的问题,首先是在手机端,可能是用户访问了一些小网站的,(你懂得),然后在访问我的网站时,会带小广告过来,通常是wifi被dns劫持的情况下导入到广告脚本, 1.处理这些要知道广告的根源 ...

  8. Python函数篇(5)-装饰器及实例讲解

    1.装饰器的概念   装饰器本质上就是一个函数,主要是为其他的函数添加附加的功能,装饰器的原则有以下两个: 装饰器不能修改被修饰函数的源代码 装饰器不能修改被修改函数的调用方式   装饰器可以简单的理 ...

  9. Linux中MySQL配置文件my.cnf参数优化

    MySQL参数优化这东西不好好研究还是比较难懂的,其实不光是MySQL,大部分程序的参数优化,是很复杂的.MySQL的参数优化也不例外,对于不同的需求,还有硬件的配置,优化不可能又最优选择,只能慢慢的 ...

  10. Java-----SSM(SpringMVC+Spring+mybaties)框架整合

    在进行整合之前,首先了解这个框架的作用 Mybaties: 丰富的标签库,可写动态sql,并统一的在.XML文件中编写,方便统一管理,解耦 SpringMVC: 标准的MVC思想(mode,view, ...