JDK8新特性:使用stream、Comparator和Method Reference实现集合的优雅排序
大家对java接口Comparator和Comparable都不陌生,JDK8里面Comparable还和以前一样,没有什么改动;但是Comparator在之前基础上增加了很多static和default方法。本文主要结合JDK的stream编程,学习下Comparator。阅读本文需要一些前置知识,可以参考如下文章。
JDK8新特性:接口的静态方法和默认方法
http://blog.csdn.net/aitangyong/article/details/54134385
JDK8新特性:函数式接口@FunctionalInterface的使用说明
http://blog.csdn.net/aitangyong/article/details/54137067
JDK8新特性:lambda入门
http://blog.csdn.net/aitangyong/article/details/54317539
JDK8新特性:使用Method References实现方法复用,简化lambda表达式
http://blog.csdn.net/aitangyong/article/details/54586197
可以使用Stream.sort对集合进行排序,sort有2个重载方法,区别如下。
- // Student实现Comparable接口,默认按照id升序排列
- public class Student implements Comparable<Student>{
- private int id;
- private int age;
- private String name;
- private Address address;
- public Student(int id, int age, String name, Address address) {
- this.id = id;
- this.age = age;
- this.name = name;
- this.address = address;
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public Address getAddress() {
- return address;
- }
- public void setAddress(Address address) {
- this.address = address;
- }
- @Override
- public String toString() {
- return "Student [id=" + id + ", age=" + age + ", name=" + name + ", address=" + address + "]";
- }
- @Override
- public int compareTo(Student o) {
- return this.id - o.id;
- }
- }
stream().sorted()/Comparator.naturalOrder()/Comparator.reverseOrder(),要求元素必须实现Comparable接口。
- import java.util.ArrayList;
- import java.util.Comparator;
- import java.util.List;
- import java.util.stream.Collectors;
- public class TestComparator {
- public static void main(String[] args) {
- List<Student> students = buildStudents();
- // 按照默认顺序排序
- List<Student> ascList1 = students.stream().sorted().collect(Collectors.toList());
- System.out.println(ascList1);
- // 按照自然序排序(其实就是默认顺序)
- List<Student> ascList2 = students.stream().sorted(Comparator.naturalOrder()).collect(Collectors.toList());
- System.out.println(ascList2);
- // 按照默认顺序的相反顺序排序
- List<Student> descList = students.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
- System.out.println(descList);
- }
- private static List<Student> buildStudents() {
- List<Student> students = new ArrayList<>();
- students.add(new Student(10, 20, "aty", new Address("d")));
- students.add(new Student(1, 22, "qun", new Address("c")));
- students.add(new Student(1, 26, "Zen", new Address("b")));
- students.add(new Student(5, 23, "aty", new Address("a")));
- return students;
- }
- }
如果Student没有实现Comparable接口,效果如下:
接下来测试,都不要求Student实现Comparable接口,这里直接给出Student和Address实体类。
- public class Student {
- private int id;
- private int age;
- private String name;
- private Address address;
- public Student(int id, int age, String name, Address address) {
- this.id = id;
- this.age = age;
- this.name = name;
- this.address = address;
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public Address getAddress() {
- return address;
- }
- public void setAddress(Address address) {
- this.address = address;
- }
- @Override
- public String toString() {
- return "Student [id=" + id + ", age=" + age + ", name=" + name + ", address=" + address + "]";
- }
- }
- public class Address {
- private String address;
- public Address(String address) {
- super();
- this.address = address;
- }
- public String getAddress() {
- return address;
- }
- public void setAddress(String address) {
- this.address = address;
- }
- @Override
- public String toString() {
- return "Address [address=" + address + "]";
- }
- }
Comparator.comparing(Function
keyExtractor)生成1个Comparator对象,要求keyExtractor.apply()返回值一定要实现Comparable接口。比如下面代码extractIdWay1和extractIdWay2都是等价的,从Student对象中提取id属性,而id是int类型(Integer实现了Comparable)。
- import java.util.ArrayList;
- import java.util.Comparator;
- import java.util.List;
- import java.util.function.Function;
- import java.util.stream.Collectors;
- public class TestComparator {
- public static void main(String[] args) {
- List<Student> students = buildStudents();
- // 使用lambda表达式创建Function对象
- Function<Student, Integer> extractIdWay1 = (student) -> student.getId();
- // 使用方法引用简化lambda
- Function<Student, Integer> extractIdWay2 = Student::getId;
- // Comparator.comparing(Function keyExtractor)
- Comparator<Student> byId = Comparator.comparing(extractIdWay2);
- // 升序
- List<Student> ascList = students.stream().sorted(byId).collect(Collectors.toList());
- System.out.println(ascList);
- // 降序
- List<Student> descList = students.stream().sorted(byId.reversed()).collect(Collectors.toList());
- System.out.println(descList);
- }
- private static List<Student> buildStudents() {
- List<Student> students = new ArrayList<>();
- students.add(new Student(10, 20, "aty", new Address("d")));
- students.add(new Student(1, 22, "qun", new Address("c")));
- students.add(new Student(1, 26, "Zen", new Address("b")));
- students.add(new Student(5, 23, "aty", new Address("a")));
- return students;
- }
- }
由于Student.getAddress()返回的对象没有实现Comparable接口,所以不能通过Comparator.comparing()创建一个Comparator对象。
如果我们想安装Address(没有实现Comparable接口)排序怎么办呢?使用另一种形式的comparing方法:
- import java.util.ArrayList;
- import java.util.Comparator;
- import java.util.List;
- import java.util.stream.Collectors;
- public class TestComparator {
- public static void main(String[] args) {
- List<Student> students = buildStudents();
- Comparator<Address> cmpAddr = Comparator.comparing(Address::getAddress);
- Comparator<Student> byAddress = Comparator.comparing(Student::getAddress, cmpAddr);
- List<Student> sortedAddressList = students.stream().sorted(byAddress).collect(Collectors.toList());
- System.out.println(sortedAddressList);
- }
- private static List<Student> buildStudents() {
- List<Student> students = new ArrayList<>();
- students.add(new Student(10, 20, "aty", new Address("d")));
- students.add(new Student(1, 22, "qun", new Address("c")));
- students.add(new Student(1, 26, "Zen", new Address("b")));
- students.add(new Student(5, 23, "aty", new Address("a")));
- return students;
- }
- }
这种形式的comparing()接收2个参数,第一个参数提取要排序的key,第二个参数指定排序的Comparator。自己指定比较器,可以灵活定制比较逻辑。比如,我们想实现字符串不区分大小写比较。
- //getName()返回String本身已经实现了Comparable,但是我们可以自己传递一个不区分大小写的比较器
- Comparator<Student> byName = Comparator.comparing(Student::getName, String.CASE_INSENSITIVE_ORDER);
- List<Student> sortedNameList = students.stream().sorted(byName).collect(Collectors.toList());
- System.out.println(sortedNameList);
comparingDouble()、comparingLong()、comparingInt()不过是comparing()更具体的版本,使用方式相同。
- public static void main(String[] args) {
- List<Student> students = buildStudents();
- Comparator<Student> byAge1 = Comparator.comparingInt(Student::getAge);
- Comparator<Student> byAge2 = Comparator.comparing(Student::getAge);
- List<Student> sortedAgeList1 = students.stream().sorted(byAge1).collect(Collectors.toList());
- List<Student> sortedAgeList2 = students.stream().sorted(byAge2).collect(Collectors.toList());
- System.out.println(sortedAgeList1);
- System.out.println(sortedAgeList2);
- }
- private static List<Student> buildStudents() {
- List<Student> students = new ArrayList<>();
- students.add(new Student(10, 20, "aty", new Address("d")));
- students.add(new Student(1, 22, "qun", new Address("c")));
- students.add(new Student(1, 26, "Zen", new Address("b")));
- students.add(new Student(5, 23, "aty", new Address("a")));
- return students;
- }
Comparator.nullsFirst()和Comparator.nullsLast(),前面我们创建的Student列表中没有null,如果有null的话,上面的代码都会抛异常。而这2个方法就是用来处理null的,一个认为null比所有非null都小,一个认为比所有都大。
- public class TestComparator {
- public static void main(String[] args) {
- List<Student> students = buildStudents();
- Comparator<Student> nullNotAllowed = Comparator.comparing(Student::getId);
- Comparator<Student> allowNullComparator = Comparator.nullsFirst(nullNotAllowed);
- // 正常排序
- List<Student> result1 = students.stream().sorted(allowNullComparator).collect(Collectors.toList());
- System.out.println(result1);
- // 抛异常
- List<Student> result2 = students.stream().sorted(nullNotAllowed).collect(Collectors.toList());
- System.out.println(result2);
- }
- private static List<Student> buildStudents() {
- List<Student> students = new ArrayList<>();
- students.add(new Student(10, 20, "aty", new Address("d")));
- students.add(new Student(1, 22, "qun", new Address("c")));
- students.add(new Student(1, 26, "Zen", new Address("b")));
- students.add(new Student(5, 23, "aty", new Address("a")));
- students.add(null);
- return students;
- }
- }
至此Comparator的static方法已经介绍完毕,接下来我们看下它的default方法。
reversed()前面已经介绍了,返回一个新的比较器(排序顺序相反)
thenComparing()系列方法与comparing()使用方法类似
如果我们先按照id排序,id相等的话再按照name排序,那么可以这样写。
- public static void main(String[] args) {
- List<Student> students = buildStudents();
- // id升序
- Comparator<Student> byIdASC = Comparator.comparing(Student::getId);
- // named不分区大小写降序
- Comparator<Student> byNameDESC = Comparator.comparing(Student::getName, String.CASE_INSENSITIVE_ORDER)
- .reversed();
- // 联合排序
- Comparator<Student> finalComparator = byIdASC.thenComparing(byNameDESC);
- List<Student> result = students.stream().sorted(finalComparator).collect(Collectors.toList());
- System.out.println(result);
- }
- private static List<Student> buildStudents() {
- List<Student> students = new ArrayList<>();
- students.add(new Student(10, 20, "aty", new Address("d")));
- students.add(new Student(1, 22, "qun", new Address("c")));
- students.add(new Student(1, 26, "Zen", new Address("b")));
- students.add(new Student(5, 23, "aty", new Address("a")));
- return students;
- }
JDK8新特性:使用stream、Comparator和Method Reference实现集合的优雅排序的更多相关文章
- JDK8新特性关于Stream流
在Java1.8之前还没有stream流式算法的时候,我们要是在一个放有多个User对象的list集合中,将每个User对象的主键ID取出,组合成一个新的集合,首先想到的肯定是遍历,如下: 1 2 3 ...
- JDK8新特性之Stream流
是什么是Stream流 java.util.stream.Stream Stream流和传统的IO流,它们都叫流,却是两个完全不一样的概念和东西. 流可以简单的说是处理数据集合的东西,可以申明式流式A ...
- JDK8新特性之stream
stream中有很多方法,讲一些常用的. 1.forEach(),遍历方法,很简单,对于一般的遍历可以替代for循环 List<String> strings = Arrays.asLis ...
- jdk8新特性---list.stream
项目中用到了该api ,记录下来 具有get set 构造方法的实体类 开始使用: 结果为: 更多可以参考: https://blog.csdn.net/justloveyou_/article/de ...
- JDK8新特性一览
转载自:http://blog.csdn.net/qiubabin/article/details/70256683 官方新特性说明地址 Jdk8新特性.png 下面对几个常用的特性做下重点说明. 一 ...
- 一次电话Java面试的问题总结(JDK8新特性、哈希冲突、HashMap原理、线程安全、Linux查询命令、Hadoop节点)
面试涉及问题含有: Java JDK8新特性 集合(哈希冲突.HashMap的原理.自动排序的集合TreeSet) 多线程安全问题 String和StringBuffer JVM 原理.运行流程.内部 ...
- JDK8 新特性
JDK8 新特性目录导航: Lambda 表达式 函数式接口 方法引用.构造器引用和数组引用 接口支持默认方法和静态方法 Stream API 增强类型推断 新的日期时间 API Optional 类 ...
- jdk8新特性
JDK8新特性(JDK8的新特性) * 接口中可以定义有方法体的方法,如果是非静态,必须用default修饰 * 如果是静态的就不用了 class Test { public void run() { ...
- java1.8新特性之stream流式算法
在Java1.8之前还没有stream流式算法的时候,我们要是在一个放有多个User对象的list集合中,将每个User对象的主键ID取出,组合成一个新的集合,首先想到的肯定是遍历,如下: List& ...
随机推荐
- VNC跨平台远程桌面的安装与使用
1.安装:yum install tigervnc-server -y 2.设置自启动: chkconfig vncserver on 3.配置文件:vim /etc/sysconfig/vncser ...
- svg_png
#!/usr/bin/env python#-*- encoding=UTF-8 -*-from __future__ import print_functionimport sysimport ra ...
- Flask之视图(一)
2.关于Flask 知识点 从Hello World开始 给路由传递参数 返回状态码 重定向 正则URL 设置cookie和获取cookie 扩展 上下文 请求钩子 Flask装饰器路由的实现 Fla ...
- PHP5缓存插件
1.1操作码介绍及缓存原理 当客户端请求一个PHP程序时,服务器的PHP引擎会解析到该PHP程序,并将其变异为特定的操作码文件(OperateCode opcode),这是要执行的PHP代码的一种二进 ...
- PEM文件和private.key文件生成IIS服务器所需的pfx文件(配置SSL用)
第一步:输入域名,点击"创建免费的SSL证书"按钮,如图 第二步:输入邮箱,点击创建,如图 创建成功后,需要进行dns验证,结果图如下: 第三步:在域名所在的云服务器上,添加域名解 ...
- 无法访问win8默认共享(如C$)解决办法
可以使用此过程允许作为本地 Administrators 组的成员并使用密码身份验证登录的用户在会话过程中使用其管理权限.启动注册表编辑器.单击“开始”,在“开始搜索”框中键入 regedit,然后按 ...
- vs2010下设置release版本调试设置
设置在Release模式下调试的方法: 1.工程项目上右键 -> 属性 2.c++ -> 常规 -〉调试信息格式 选 程序数据库(/Zi)或(/ZI), 注意:如果是库的话,只能( ...
- eclipse egit 更新未提交文件的方法
问题 以前一直在项目中使用SVN..感觉真的很好用,不管是切分支,合并分支,编辑冲突,还是更新代码什么的都感觉很方便...现在新公司项目里使用git...我自己虽然用过,但是多人合作一直没用过.所以遇 ...
- Python 调用阿里云 API 收集 ECS 数据
#!/usr/bin/env python # coding: utf-8 # author: Wang XiaoQiang ''' 功能介绍: 1.调用阿里云API,收集所有区域 ECS 信息 2. ...
- ios学习--详解IPhone动画效果类型及实现方法
详解IPhone动画效果类型及实现方法是本文要介绍的内容,主要介绍了iphone中动画的实现方法,不多说,我们一起来看内容. 实现iphone漂亮的动画效果主要有两种方法,一种是UIView层面的,一 ...