简介 我们在java编码的过程中不可避免的会使用java中提供的集合对象,例如List,ArrayList等等,那么在使用集合的过程中有很大的几率会用到排序.回想一下在学习c语言的时候排序是如何实现的? 没错,就是通过手写各种基础的排序算法实现排序(当然现在也有各种c语言的基础包可用),例如选择\冒泡\插入\希尔\二分法等等.在Java中我们当然也可以自己写排序算法实现排序,但是JDK已经提供一些基础的基于集合的排序接口供我们使用,因此无需特意自写算法.本文将介绍两种排序的实现方式Comparable与Comparator.
Comparable 首先我们举一个简单的例子,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Test public void integerSort () { List<Integer> integers = Lists.newArrayList(); integers.add(5 ); integers.add(6 ); integers.add(1 ); integers.add(3 ); integers.add(4 ); integers.add(9 ); printUtil.printList(integers); Collections.sort(integers); printUtil.printList(integers); }
执行结果如下:
1 2 5 ,6 ,1 ,3 ,4 ,9 ,1 ,3 ,4 ,5 ,6 ,9 ,
看到这里大家或许会有疑问?我们什么都没有做为什么代码自动实现了排序?
那是因为我们的包装类型Integer内部已经继承并重写了comparable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public final class Integer extends Number implements Comparable <Integer > { public int compareTo (Integer anotherInteger) { return compare(this .value, anotherInteger.value); } public static int compare (int x, int y) { return (x < y) ? -1 : ((x == y) ? 0 : 1 ); } }
我们再来举例一个稍微复杂的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class student implements Comparable <student > { int age; int grade; @Override public int compareTo (student o) { if (o.getAge() > this .getAge()){ return -1 ; }else if (o.getAge() < this .getAge()){ return 1 ; }else { return 0 ; } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Test public void studentSortByAge () { List<student> students = Lists.newArrayList(); students.add(student.builder().age(10 ).build()); students.add(student.builder().age(30 ).build()); students.add(student.builder().age(50 ).build()); students.add(student.builder().age(20 ).build()); students.add(student.builder().age(40 ).build()); students.add(student.builder().age(60 ).build()); printUtil.printList(students); Collections.sort(students); printUtil.printList(students); }
输出结果如下:
1 2 3 4 排序前 student(age=10 , grade=0 ),student(age=30 , grade=0 ),student(age=50 , grade=0 ),student(age=20 , grade=0 ),student(age=40 , grade=0 ),student(age=60 , grade=0 ) 排序后 student(age=10 , grade=0 ),student(age=20 , grade=0 ),student(age=30 , grade=0 ),student(age=40 , grade=0 ),student(age=50 , grade=0 ),student(age=60 , grade=0 )
针对这种复杂的自定义模型排序我们需要模型自己继承和重写compareTo方法,很多人第一次接触该方法或许会感到疑惑,返回值只有三种(1、-1、0)分别代表什么含义?为什么这种纯数字的返回值就可以实现排序呢?
首先要解答的是为什么返回1、-1、0就可以实现排序?
答:comparable接口本质上是为类提供的比较接口,可以为类附加比较功能,比较的结果要么相等要么存在大小关系。借助比较结果,第三方就可以实现排序功能,例如Collections.sort()就是利用二分法实现的排序,stream().sorted()利用的流处理进行排序,无论是哪一种排序归根揭底必须借助comparable接口提供的比较功能。
其次我们要理解1、-1、0分别代表什么含义?
上文代码注释中已经有比较详细的解答,大家可以思考和领悟一下。这里做一下简单的叙述:简单来说无论我们希望倒叙排序还是正序排序只需要调整好o.getAge()与this.getAge()的大小关系即可。想象一下有一列数组,o表示数组中的前一位,this代表数组中的后一位,如果我们希望升序排序,只需要设定o.getAge()<this.getAge()时返回1即可,同样若需要降序排序则只需要设定o.getAge()>this.getAge()时返回1即可。说到这里大家应该明白数字的含义了,本质上就是比较结果的标记。
总结
我们现在来总结一下Comparable的作用和用法,其作为接口为类提供了内部比较功能,使继承了该接口的类不用去依赖外部的比较器就可以实现同类对象的比较。由于实现了类的比较那么我们可以轻而易举的实现类的排序。
Comparator 老样子,想要讲解透彻用法还是要代码举例,我们先把必要的比较器对象创建好,具体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 private List<student> students = Lists.newArrayList();Comparator<student> studentComparator_desc = new Comparator<student>() { @Override public int compare (student o1, student o2) { if (o1.getAge() > o2.getAge()) { return -1 ; } else if (o1.getAge() < o2.getAge()) { return 1 ; } else { return 0 ; } } }; Comparator<student> studentComparator_asc = new Comparator<student>() { @Override public int compare (student o1, student o2) { if (o1.getAge() > o2.getAge()) { return 1 ; } else if (o1.getAge() < o2.getAge()) { return -1 ; } else { return 0 ; } } }; Comparator<student> studentComparator_grade_asc = new Comparator<student>() { @Override public int compare (student o1, student o2) { if (o1.getGrade() > o2.getGrade()) { return 1 ; } else if (o1.getGrade() < o2.getGrade()) { return -1 ; } else { return 0 ; } } }; Comparator<Integer> studentComparator_grade_desc = new Comparator<Integer>() { @Override public int compare (Integer o1, Integer o2) { if (o1 < o2) { return 1 ; } else if (o1 > o2) { return -1 ; } else { return 0 ; } } };
接下来我们编写测试代码一窥究竟,先从简单比较方式开始,具体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @Test public void publicSortMethod () { students.add(student.builder().age(10 ).build()); students.add(student.builder().age(30 ).build()); students.add(student.builder().age(50 ).build()); students.add(student.builder().age(10 ).build()); students.add(student.builder().age(20 ).build()); students.add(student.builder().age(40 ).build()); students.add(student.builder().age(60 ).build()); printUtil.printList(students); students = students.stream().sorted(studentComparator_desc).collect(Collectors.toList()); printUtil.printList(students); }
Comparator的高级用法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 @Test public void otherSortMethod () { students.add(student.builder().age(10 ).grade(200 ).build()); students.add(student.builder().age(30 ).build()); students.add(student.builder().age(50 ).build()); students.add(student.builder().age(10 ).build()); students.add(student.builder().age(20 ).grade(500 ).build()); students.add(student.builder().age(40 ).build()); students.add(student.builder().age(60 ).build()); printUtil.printList(students); students.sort(Comparator.comparingInt(e -> e.getAge())); printUtil.printList(students); }
总结
以上两段测试代码已经将Comparator讲解的比较透彻,总的来说Comparator是用来定义比较器对象的,适合原本不支持比较的类对象实现比较功能.对比较器(1,0,-1)的详细解说可参考[1]
参考:
[1]https://www.cnblogs.com/maozp/p/11153403.html
版权声明:本文为博主原创文章,欢迎转载,转载请注明作者、原文超链接,感谢各位看官!!!
本文出自: monkeyGeek
座右铭: 生于忧患,死于安乐
欢迎志同道合的朋友一起交流、探讨!
monkeyGeek