大家对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个重载方法,区别如下。

  1. // Student实现Comparable接口,默认按照id升序排列
  2. public class Student implements Comparable<Student>{
  3.  
  4. private int id;
  5.  
  6. private int age;
  7.  
  8. private String name;
  9.  
  10. private Address address;
  11.  
  12. public Student(int id, int age, String name, Address address) {
  13. this.id = id;
  14. this.age = age;
  15. this.name = name;
  16. this.address = address;
  17. }
  18.  
  19. public int getId() {
  20. return id;
  21. }
  22.  
  23. public void setId(int id) {
  24. this.id = id;
  25. }
  26.  
  27. public int getAge() {
  28. return age;
  29. }
  30.  
  31. public void setAge(int age) {
  32. this.age = age;
  33. }
  34.  
  35. public String getName() {
  36. return name;
  37. }
  38.  
  39. public void setName(String name) {
  40. this.name = name;
  41. }
  42.  
  43. public Address getAddress() {
  44. return address;
  45. }
  46.  
  47. public void setAddress(Address address) {
  48. this.address = address;
  49. }
  50.  
  51. @Override
  52. public String toString() {
  53. return "Student [id=" + id + ", age=" + age + ", name=" + name + ", address=" + address + "]";
  54. }
  55.  
  56. @Override
  57. public int compareTo(Student o) {
  58. return this.id - o.id;
  59. }
  60.  
  61. }

stream().sorted()/Comparator.naturalOrder()/Comparator.reverseOrder(),要求元素必须实现Comparable接口。

  1. import java.util.ArrayList;
  2. import java.util.Comparator;
  3. import java.util.List;
  4. import java.util.stream.Collectors;
  5.  
  6. public class TestComparator {
  7.  
  8. public static void main(String[] args) {
  9. List<Student> students = buildStudents();
  10.  
  11. // 按照默认顺序排序
  12. List<Student> ascList1 = students.stream().sorted().collect(Collectors.toList());
  13. System.out.println(ascList1);
  14.  
  15. // 按照自然序排序(其实就是默认顺序)
  16. List<Student> ascList2 = students.stream().sorted(Comparator.naturalOrder()).collect(Collectors.toList());
  17. System.out.println(ascList2);
  18.  
  19. // 按照默认顺序的相反顺序排序
  20. List<Student> descList = students.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
  21. System.out.println(descList);
  22.  
  23. }
  24.  
  25. private static List<Student> buildStudents() {
  26. List<Student> students = new ArrayList<>();
  27. students.add(new Student(10, 20, "aty", new Address("d")));
  28. students.add(new Student(1, 22, "qun", new Address("c")));
  29. students.add(new Student(1, 26, "Zen", new Address("b")));
  30. students.add(new Student(5, 23, "aty", new Address("a")));
  31. return students;
  32. }
  33.  
  34. }
  35.  
  36.  


如果Student没有实现Comparable接口,效果如下:

接下来测试,都不要求Student实现Comparable接口,这里直接给出Student和Address实体类。

  1. public class Student {
  2.  
  3. private int id;
  4.  
  5. private int age;
  6.  
  7. private String name;
  8.  
  9. private Address address;
  10.  
  11. public Student(int id, int age, String name, Address address) {
  12. this.id = id;
  13. this.age = age;
  14. this.name = name;
  15. this.address = address;
  16. }
  17.  
  18. public int getId() {
  19. return id;
  20. }
  21.  
  22. public void setId(int id) {
  23. this.id = id;
  24. }
  25.  
  26. public int getAge() {
  27. return age;
  28. }
  29.  
  30. public void setAge(int age) {
  31. this.age = age;
  32. }
  33.  
  34. public String getName() {
  35. return name;
  36. }
  37.  
  38. public void setName(String name) {
  39. this.name = name;
  40. }
  41.  
  42. public Address getAddress() {
  43. return address;
  44. }
  45.  
  46. public void setAddress(Address address) {
  47. this.address = address;
  48. }
  49.  
  50. @Override
  51. public String toString() {
  52. return "Student [id=" + id + ", age=" + age + ", name=" + name + ", address=" + address + "]";
  53. }
  54.  
  55. }
  1. public class Address {
  2. private String address;
  3.  
  4. public Address(String address) {
  5. super();
  6. this.address = address;
  7. }
  8.  
  9. public String getAddress() {
  10. return address;
  11. }
  12.  
  13. public void setAddress(String address) {
  14. this.address = address;
  15. }
  16.  
  17. @Override
  18. public String toString() {
  19. return "Address [address=" + address + "]";
  20. }
  21.  
  22.  
  23. }

Comparator.comparing(Function
keyExtractor)生成1个Comparator对象,要求keyExtractor.apply()返回值一定要实现Comparable接口。比如下面代码extractIdWay1和extractIdWay2都是等价的,从Student对象中提取id属性,而id是int类型(Integer实现了Comparable)。

  1. import java.util.ArrayList;
  2. import java.util.Comparator;
  3. import java.util.List;
  4. import java.util.function.Function;
  5. import java.util.stream.Collectors;
  6.  
  7. public class TestComparator {
  8.  
  9. public static void main(String[] args) {
  10. List<Student> students = buildStudents();
  11.  
  12. // 使用lambda表达式创建Function对象
  13. Function<Student, Integer> extractIdWay1 = (student) -> student.getId();
  14.  
  15. // 使用方法引用简化lambda
  16. Function<Student, Integer> extractIdWay2 = Student::getId;
  17.  
  18. // Comparator.comparing(Function keyExtractor)
  19. Comparator<Student> byId = Comparator.comparing(extractIdWay2);
  20.  
  21. // 升序
  22. List<Student> ascList = students.stream().sorted(byId).collect(Collectors.toList());
  23. System.out.println(ascList);
  24.  
  25. // 降序
  26. List<Student> descList = students.stream().sorted(byId.reversed()).collect(Collectors.toList());
  27. System.out.println(descList);
  28.  
  29. }
  30.  
  31. private static List<Student> buildStudents() {
  32. List<Student> students = new ArrayList<>();
  33. students.add(new Student(10, 20, "aty", new Address("d")));
  34. students.add(new Student(1, 22, "qun", new Address("c")));
  35. students.add(new Student(1, 26, "Zen", new Address("b")));
  36. students.add(new Student(5, 23, "aty", new Address("a")));
  37. return students;
  38. }
  39.  
  40. }

由于Student.getAddress()返回的对象没有实现Comparable接口,所以不能通过Comparator.comparing()创建一个Comparator对象。

如果我们想安装Address(没有实现Comparable接口)排序怎么办呢?使用另一种形式的comparing方法:

  1. import java.util.ArrayList;
  2. import java.util.Comparator;
  3. import java.util.List;
  4. import java.util.stream.Collectors;
  5.  
  6. public class TestComparator {
  7.  
  8. public static void main(String[] args) {
  9. List<Student> students = buildStudents();
  10.  
  11. Comparator<Address> cmpAddr = Comparator.comparing(Address::getAddress);
  12. Comparator<Student> byAddress = Comparator.comparing(Student::getAddress, cmpAddr);
  13. List<Student> sortedAddressList = students.stream().sorted(byAddress).collect(Collectors.toList());
  14. System.out.println(sortedAddressList);
  15. }
  16.  
  17. private static List<Student> buildStudents() {
  18. List<Student> students = new ArrayList<>();
  19. students.add(new Student(10, 20, "aty", new Address("d")));
  20. students.add(new Student(1, 22, "qun", new Address("c")));
  21. students.add(new Student(1, 26, "Zen", new Address("b")));
  22. students.add(new Student(5, 23, "aty", new Address("a")));
  23. return students;
  24. }
  25.  
  26. }

这种形式的comparing()接收2个参数,第一个参数提取要排序的key,第二个参数指定排序的Comparator。自己指定比较器,可以灵活定制比较逻辑。比如,我们想实现字符串不区分大小写比较。

  1. //getName()返回String本身已经实现了Comparable,但是我们可以自己传递一个不区分大小写的比较器
  2. Comparator<Student> byName = Comparator.comparing(Student::getName, String.CASE_INSENSITIVE_ORDER);
  3. List<Student> sortedNameList = students.stream().sorted(byName).collect(Collectors.toList());
  4. System.out.println(sortedNameList);

comparingDouble()、comparingLong()、comparingInt()不过是comparing()更具体的版本,使用方式相同。

  1. public static void main(String[] args) {
  2. List<Student> students = buildStudents();
  3.  
  4. Comparator<Student> byAge1 = Comparator.comparingInt(Student::getAge);
  5. Comparator<Student> byAge2 = Comparator.comparing(Student::getAge);
  6. List<Student> sortedAgeList1 = students.stream().sorted(byAge1).collect(Collectors.toList());
  7. List<Student> sortedAgeList2 = students.stream().sorted(byAge2).collect(Collectors.toList());
  8. System.out.println(sortedAgeList1);
  9. System.out.println(sortedAgeList2);
  10. }
  11.  
  12. private static List<Student> buildStudents() {
  13. List<Student> students = new ArrayList<>();
  14. students.add(new Student(10, 20, "aty", new Address("d")));
  15. students.add(new Student(1, 22, "qun", new Address("c")));
  16. students.add(new Student(1, 26, "Zen", new Address("b")));
  17. students.add(new Student(5, 23, "aty", new Address("a")));
  18. return students;
  19. }

Comparator.nullsFirst()和Comparator.nullsLast(),前面我们创建的Student列表中没有null,如果有null的话,上面的代码都会抛异常。而这2个方法就是用来处理null的,一个认为null比所有非null都小,一个认为比所有都大。

  1. public class TestComparator {
  2.  
  3. public static void main(String[] args) {
  4. List<Student> students = buildStudents();
  5. Comparator<Student> nullNotAllowed = Comparator.comparing(Student::getId);
  6. Comparator<Student> allowNullComparator = Comparator.nullsFirst(nullNotAllowed);
  7.  
  8. // 正常排序
  9. List<Student> result1 = students.stream().sorted(allowNullComparator).collect(Collectors.toList());
  10. System.out.println(result1);
  11.  
  12. // 抛异常
  13. List<Student> result2 = students.stream().sorted(nullNotAllowed).collect(Collectors.toList());
  14. System.out.println(result2);
  15.  
  16. }
  17.  
  18. private static List<Student> buildStudents() {
  19. List<Student> students = new ArrayList<>();
  20. students.add(new Student(10, 20, "aty", new Address("d")));
  21. students.add(new Student(1, 22, "qun", new Address("c")));
  22. students.add(new Student(1, 26, "Zen", new Address("b")));
  23. students.add(new Student(5, 23, "aty", new Address("a")));
  24. students.add(null);
  25. return students;
  26. }
  27.  
  28. }

至此Comparator的static方法已经介绍完毕,接下来我们看下它的default方法。

reversed()前面已经介绍了,返回一个新的比较器(排序顺序相反)

thenComparing()系列方法与comparing()使用方法类似

如果我们先按照id排序,id相等的话再按照name排序,那么可以这样写。

  1. public static void main(String[] args) {
  2. List<Student> students = buildStudents();
  3.  
  4. // id升序
  5. Comparator<Student> byIdASC = Comparator.comparing(Student::getId);
  6.  
  7. // named不分区大小写降序
  8. Comparator<Student> byNameDESC = Comparator.comparing(Student::getName, String.CASE_INSENSITIVE_ORDER)
  9. .reversed();
  10.  
  11. // 联合排序
  12. Comparator<Student> finalComparator = byIdASC.thenComparing(byNameDESC);
  13.  
  14. List<Student> result = students.stream().sorted(finalComparator).collect(Collectors.toList());
  15. System.out.println(result);
  16. }
  17.  
  18. private static List<Student> buildStudents() {
  19. List<Student> students = new ArrayList<>();
  20. students.add(new Student(10, 20, "aty", new Address("d")));
  21. students.add(new Student(1, 22, "qun", new Address("c")));
  22. students.add(new Student(1, 26, "Zen", new Address("b")));
  23. students.add(new Student(5, 23, "aty", new Address("a")));
  24. return students;
  25. }

JDK8新特性:使用stream、Comparator和Method Reference实现集合的优雅排序的更多相关文章

  1. JDK8新特性关于Stream流

    在Java1.8之前还没有stream流式算法的时候,我们要是在一个放有多个User对象的list集合中,将每个User对象的主键ID取出,组合成一个新的集合,首先想到的肯定是遍历,如下: 1 2 3 ...

  2. JDK8新特性之Stream流

    是什么是Stream流 java.util.stream.Stream Stream流和传统的IO流,它们都叫流,却是两个完全不一样的概念和东西. 流可以简单的说是处理数据集合的东西,可以申明式流式A ...

  3. JDK8新特性之stream

    stream中有很多方法,讲一些常用的. 1.forEach(),遍历方法,很简单,对于一般的遍历可以替代for循环 List<String> strings = Arrays.asLis ...

  4. jdk8新特性---list.stream

    项目中用到了该api ,记录下来 具有get set 构造方法的实体类 开始使用: 结果为: 更多可以参考: https://blog.csdn.net/justloveyou_/article/de ...

  5. JDK8新特性一览

    转载自:http://blog.csdn.net/qiubabin/article/details/70256683 官方新特性说明地址 Jdk8新特性.png 下面对几个常用的特性做下重点说明. 一 ...

  6. 一次电话Java面试的问题总结(JDK8新特性、哈希冲突、HashMap原理、线程安全、Linux查询命令、Hadoop节点)

    面试涉及问题含有: Java JDK8新特性 集合(哈希冲突.HashMap的原理.自动排序的集合TreeSet) 多线程安全问题 String和StringBuffer JVM 原理.运行流程.内部 ...

  7. JDK8 新特性

    JDK8 新特性目录导航: Lambda 表达式 函数式接口 方法引用.构造器引用和数组引用 接口支持默认方法和静态方法 Stream API 增强类型推断 新的日期时间 API Optional 类 ...

  8. jdk8新特性

    JDK8新特性(JDK8的新特性) * 接口中可以定义有方法体的方法,如果是非静态,必须用default修饰 * 如果是静态的就不用了 class Test { public void run() { ...

  9. java1.8新特性之stream流式算法

    在Java1.8之前还没有stream流式算法的时候,我们要是在一个放有多个User对象的list集合中,将每个User对象的主键ID取出,组合成一个新的集合,首先想到的肯定是遍历,如下: List& ...

随机推荐

  1. 由于安装Android设备驱动异常,ADB无法识别安卓设备的解决方案

    体验更优排版请移步原文:http://blog.kwin.wang/programming/android-driver-exception-solution.html 最近换了台新电脑,在Andro ...

  2. 学习 Python 编程的 19 个资源 (转)

    学习 Python 编程的 19 个资源 2018-01-07 数据与算法之美 编译:wzhvictor,英文:codecondo segmentfault.com/a/119000000418731 ...

  3. jquery 三元运算

    三元运算: 条件  ? 条件为真取此值 : 条件为假取此值; var v = $(:check).prop('checked')?faule:true; $(:check).prop('checked ...

  4. 控件的WndProc WindowProc

    SubClassWndProc This example shows how to use the WndProc method and the WindowProc property to subc ...

  5. Android apk couldn't install

    an existing package with the same name and signature is already installed

  6. mysql彻底删除

    yum remove mysql mysql-server mysql-libs compat-mysql51rm -rf /var/lib/mysqlrm /etc/my.cnf查看是否还有mysq ...

  7. ffmpeg源码分析二:main函数和transcode函数 (转2)

    原帖地址:http://blog.csdn.net/austinblog/article/details/24804455 首先从main函数看起,关键解释部分已加注释,该函数在ffmpeg.c文件中 ...

  8. C语言清空输入缓冲区的N种方法对比(转)

    C语言中有几个基本输入函数: //获取字符系列 int fgetc(FILE *stream); int getc(FILE *stream); int getchar(void); //获取行系列 ...

  9. docker学习记录1

    起因 现在自己学习微服务,服务器越来越多,虽然自己写了一些shell脚本来安装需要的软件,比如mysql,redis,jdk等等,但是还是好麻烦.希望学习docker能够快速安装部署这些东西. 记录一 ...

  10. 利用Surprise包进行电影推荐

    Surprise(Simple Python Recommendation System Engine)是一款推荐系统库,是scikit系列中的一个.简单易用,同时支持多种推荐算法(基础算法.协同过滤 ...