Java Comparable Comparator

Comparable 简介

Comparable 是排序接口。
若一个类实现了Comparable接口,就意味着“该类支持排序”。此外,“实现Comparable接口的类的对象”可以用作“有序映射(如TreeMap)”中的键或“有序集合(TreeSet)”中的元素,而不需要指定比较器。接口中通过x.compareTo(y)来比较x和y的大小。若返回负数,意味着x比y小;返回零,意味着x等于y;返回正数,意味着x大于y。

Comparator 简介

Comparator 是比较器接口。若需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口);那么,可以建立一个“该类的比较器”来进行排序。这个“比较器”只需要实现Comparator接口即可。也就是说,可以通过“实现Comparator类来新建一个比较器”,然后通过该比较器对类进行排序。
int compare(T o1, T o2)和上面的x.compareTo(y)类似,定义排序规则后返回正数,零和负数分别代表大于,等于和小于。

两者的联系

Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
代码实现

  1. import java.util.ArrayList;
  2. import java.util.Collections;
  3. import java.util.Comparator;
  4. import java.util.List;
  5. /**
  6. * @ _ooOoo_
  7. * o8888888o
  8. * 88" . "88
  9. * (| -_- |)
  10. * O\ = /O
  11. * ____/`---'\____
  12. * .' \\| |// `.
  13. * / \\||| : |||// \
  14. * / _||||| -:- |||||- \
  15. * | | \\\ - /// | |
  16. * | \_| ''\---/'' | |
  17. * \ .-\__ `-` ___/-. /
  18. * ___`. .' /--.--\ `. . __
  19. * ."" '< `.___\_<|>_/___.' >'"".
  20. * | | : `- \`.;`\ _ /`;.`/ - ` : | |
  21. * \ \ `-. \_ __\ /__ _/ .-` / /
  22. * ======`-.____`-.___\_____/___.-`____.-'======
  23. * `=---='
  24. * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  25. * 佛祖保佑 永无BUG
  26. *@DESCRIPTION Comparable是排序接口;若一个类实现了Comparable接口,就意味着“该类支持排序”。
  27. * Comparable相当于“内部比较器”
  28. *@PACKAGE_NAME com.github.compare
  29. **/
  30. public class ComparableAndCompartor
  31. {
  32. public static void main(String[] args)
  33. {
  34. List<House> houses = new ArrayList();
  35. House h1 = new House(95.0, 12000);
  36. House h2 = new House(110.0, 12160);
  37. House h3 = new House(80.0, 16300);
  38. House h4 = new House(150.3, 10690);
  39. houses.add(h1);
  40. houses.add(h2);
  41. houses.add(h3);
  42. houses.add(h4);
  43. comparable(houses);
  44. comparator(houses);
  45. }
  46. /**
  47. *@DESCRIPTION House类实现类Comparable接口, 并重写了compareTo方法, 所以执行Collections.sort方法时会去调用我们重写的compareTo方法
  48. *@CLASS_NAME ComparableAndCompartor
  49. **/
  50. private static void comparable(List houses)
  51. {
  52. System.out.printf("未排序前的顺序,%s\n", houses);
  53. Collections.sort(houses);
  54. System.out.printf("按面积大小排序后的顺序,%s\n", houses);
  55. }
  56. private static void comparator(List houses)
  57. {
  58. System.out.printf("未排序前的顺序,%s\n", houses);
  59. Collections.sort(houses, new ComparatorDetail());
  60. System.out.printf("按单价大小排序后的顺序,%s\n", houses);
  61. }
  62. /**
  63. *@DESCRIPTION 实现Compatator接口, 并重写compare方法, 根据单价倒序排序
  64. *@CLASS_NAME ComparableAndCompartor
  65. **/
  66. static class ComparatorDetail implements Comparator<House>
  67. {
  68. @Override
  69. public int compare(House o1, House o2)
  70. {
  71. if (o1.price < o2.price)
  72. return 1;
  73. else if (o1.price > o2.price)
  74. return -1;
  75. return 0;
  76. }
  77. }
  78. }
  1. package com.github.compare;
  2. /**
  3. * @ _ooOoo_
  4. * o8888888o
  5. * 88" . "88
  6. * (| -_- |)
  7. * O\ = /O
  8. * ____/`---'\____
  9. * .' \\| |// `.
  10. * / \\||| : |||// \
  11. * / _||||| -:- |||||- \
  12. * | | \\\ - /// | |
  13. * | \_| ''\---/'' | |
  14. * \ .-\__ `-` ___/-. /
  15. * ___`. .' /--.--\ `. . __
  16. * ."" '< `.___\_<|>_/___.' >'"".
  17. * | | : `- \`.;`\ _ /`;.`/ - ` : | |
  18. * \ \ `-. \_ __\ /__ _/ .-` / /
  19. * ======`-.____`-.___\_____/___.-`____.-'======
  20. * `=---='
  21. * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  22. * 佛祖保佑 永无BUG
  23. *@DESCRIPTION 一个房子对象, 有面积和单价两个属性
  24. *@PACKAGE_NAME com.github.compare
  25. **/
  26. public class House implements Comparable<House>
  27. {
  28. /*房子的面积*/
  29. protected double proportion;
  30. /*房子每平米的售价*/
  31. protected double price;
  32. public House(double proportion, double price)
  33. {
  34. this.proportion = proportion;
  35. this.price = price;
  36. }
  37. /**
  38. *@DESCRIPTION 重写compareTo方法, 利用房子的面积来进行大小比较
  39. *@CLASS_NAME House
  40. **/
  41. @Override
  42. public int compareTo(House o)
  43. {
  44. /*当前对象的面积大,返回正数*/
  45. if (this.proportion > o.proportion)
  46. return 1;
  47. /*当前面积小,返回负数*/
  48. else if (this.proportion < o.proportion)
  49. return -1;
  50. /*相等返回0*/
  51. return 0;
  52. }
  53. @Override
  54. public String toString()
  55. {
  56. return "面积为" + proportion + "\t价格为" + price;
  57. }
  58. }

附注

CollectionCollections的区别

Collection是集合类的上级接口,继承与他有关的接口主要有ListSet
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全等操作

  1. public static void main(String args[]) {
  2. //注意List是实现Collection接口的
  3. List list = new ArrayList();
  4. double array[] = { 112, 111, 23, 456, 231 };
  5. for (int i = 0; i < array.length; i++) {
  6. list.add(new Double(array[i]));
  7. }
  8. Collections.sort(list); //把list按从小到大排序
  9. for (int i = 0; i < array.length; i++) {
  10. System.out.println(list.get(i));
  11. }
  12. // 结果:23.0 111.0 112.0 231.0 456.0
  13. }

Collections如何调用重写的compareTo方法的

集合框架中,Collections工具类支持两种排序方法:

  1. Collections.sort(List<T> list);
  2. Collections.sort(List<T> list, Comparator<? super T> c)

如果待排序的列表中是数字或者字符,可以直接使用Collections.sort(list);当需要排序的集合或数组不是单纯的数字型时,需要自己定义排序规则,实现一个Comparator比较器。
Collections调用Collections.sort(list)方法,方法传递一个List集合,这里要求,List泛型里面装的元素必须实现Compareable接口此外,列表中的所有元素都必须是可相互比较的(也就是说,对于列表中的任何 e1 和 e2 元素,e1.compareTo(e2) 不得抛出 ClassCastException)。
Java源码里是这样写的

  1. All elements in the list must implement the {@link Comparable}interface.Furthermore, all elements in the list must be <i>mutually comparable</i> (that is, {@code e1.compareTo(e2)} must not throw a {@code ClassCastException} for any elements

Collections.sort源码

  1. public static <T extends Comparable<? super T>> void sort(List<T> list) {
  2. Object[] a = list.toArray();
  3. Arrays.sort(a);
  4. ListIterator<T> i = list.listIterator();
  5. for (int j=0; j<a.length; j++) {
  6. i.next();
  7. i.set((T)a[j]);
  8. }
  9. }

由源码可以看出来,sort内部调用了Arrays.sort的方法,继续向下看

Arrays.sort源码

  1. public static void sort(Object[] a) {
  2. if (LegacyMergeSort.userRequested)
  3. legacyMergeSort(a);
  4. else
  5. ComparableTimSort.sort(a);
  6. }

源码里首先判断是否采用传统的排序方法,LegacyMergeSort.userRequested属性默认为false,也就是说默认选中 ComparableTimSort.sort(a)方法(传统归并排序在1.5及之前是默认排序方法,1.5之后默认执行ComparableTimSort.sort()方法。除非程序中强制要求使用传统归并排序,语句如下:

  1. System.setProperty("java.util.Arrays.useLegacyMergeSort", "true"))

继续看 ComparableTimSort.sort(a)源码

ComparableTimSort.sort(a)源码

  1. static void sort(Object[] a) {
  2. sort(a, 0, a.length);
  3. }
  4. static void sort(Object[] a, int lo, int hi) {
  5. rangeCheck(a.length, lo, hi);
  6. int nRemaining = hi - lo;
  7. if (nRemaining < 2)
  8. return; // Arrays of size 0 and 1 are always sorted
  9. // If array is small, do a "mini-TimSort" with no merges
  10. if (nRemaining < MIN_MERGE) {
  11. int initRunLen = countRunAndMakeAscending(a, lo, hi);
  12. binarySort(a, lo, hi, lo + initRunLen);
  13. return;
  14. }
  15. /**
  16. * March over the array once, left to right, finding natural runs,
  17. * extending short natural runs to minRun elements, and merging runs
  18. * to maintain stack invariant.
  19. */
  20. ComparableTimSort ts = new ComparableTimSort(a);
  21. int minRun = minRunLength(nRemaining);
  22. do {
  23. // Identify next run
  24. int runLen = countRunAndMakeAscending(a, lo, hi);
  25. // If run is short, extend to min(minRun, nRemaining)
  26. if (runLen < minRun) {
  27. int force = nRemaining <= minRun ? nRemaining : minRun;
  28. binarySort(a, lo, lo + force, lo + runLen);
  29. runLen = force;
  30. }
  31. // Push run onto pending-run stack, and maybe merge
  32. ts.pushRun(lo, runLen);
  33. ts.mergeCollapse();
  34. // Advance to find next run
  35. lo += runLen;
  36. nRemaining -= runLen;
  37. } while (nRemaining != 0);
  38. // Merge all remaining runs to complete sort
  39. assert lo == hi;
  40. ts.mergeForceCollapse();
  41. assert ts.stackSize == 1;
  42. }

nRemaining表示没有排序的对象个数,方法执行前,如果这个数小于2,就不需要排序了。
如果2<= nRemaining <=32,即MIN_MERGE的初始值,表示需要排序的数组是小数组,可以使用mini-TimSort方法进行排序,否则需要使用归并排序。
mini-TimSort排序方法:先找出数组中从下标为0开始的第一个升序序列,或者找出降序序列后转换为升序重新放入数组,将这段升序数组作为初始数组,将之后的每一个元素通过二分法排序插入到初始数组中。注意,这里就调用到了重写的compareTo()方法了。

  1. private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {
  2. assert lo < hi;
  3. int runHi = lo + 1;
  4. if (runHi == hi)
  5. return 1;
  6. // Find end of run, and reverse range if descending
  7. if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) { // Descending
  8. while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)
  9. runHi++;
  10. reverseRange(a, lo, runHi);
  11. } else { // Ascending
  12. while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) >= 0)
  13. runHi++;
  14. }
  15. return runHi - lo;
  16. }