源码分析:

Collections.sort中:

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

可以发现,最终还是使用了Arrays.sort(a);的,不同的是:一个针对数组,一个针对集合

扩展:不同版本的内部实现问题

JDK1.6以下的时候:调用sort方法时,默认是使用mergeSort的算法
JDK1.7后,使用TimSort的算法。源码如下:
JDK7的sort方法:

 
public static void sort(Object[] a) {  
        if (LegacyMergeSort.userRequested)  
            legacyMergeSort(a);  
        else  
            ComparableTimSort.sort(a);  
    }  
    /** To be removed in a future release. */  
    private static void legacyMergeSort(Object[] a) {  
        Object[] aux = a.clone();  
        mergeSort(aux, a, 0, a.length, 0);  
    }

JDK6以下的sort方法:

 
public static void sort(Object[] a) {  
        Object[] aux = (Object[])a.clone();  
        mergeSort(aux, a, 0, a.length, 0);  
    }

当然可以使用下列的方式,在JDK7依旧使用mergeSort算法:

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

但是,从注释中可以发现,以后将让TimSort代替mergeSort

根据这篇JDK7中的排序算法详解—Collections.sort和Arrays.sort找到了解决方法:

而在Java 7中,内部实现换成了TimSort,其对对象间比较的实现要求更加严格:
Comparator的实现必须保证以下几点(出自这儿)):

  1. sgn(compare(x, y)) == -sgn(compare(y, x))
  2. (compare(x, y)>0) && (compare(y, z)>0) 意味着 compare(x, z)>0
  3. compare(x, y)==0 意味着对于任意的z:sgn(compare(x, z))==sgn(compare(y, z)) 均成立

所以,compare要相应的改成:

 
public int compare(ComparatorTest o1, ComparatorTest o2) {
    return o1.getValue() == o2.getValue() ? 0 : 
                (o1.getValue() > o2.getValue() ? 1 : -1);
}

先对相等的情况判断,再对大小的判断。

算法分析

既然这个算法比之前快排要快,那么肯定有它的巧妙之处,我们来仔细看看吧。

算法步骤

1.对于很小的数组(长度小于27),会使用插入排序。
2.选择两个点P1,P2作为轴心,比如我们可以使用第一个元素和最后一个元素。
3.P1必须比P2要小,否则将这两个元素交换,现在将整个数组分为四部分:
(1)第一部分:比P1小的元素。
(2)第二部分:比P1大但是比P2小的元素。
(3)第三部分:比P2大的元素。
(4)第四部分:尚未比较的部分。
在开始比较前,除了轴点,其余元素几乎都在第四部分,直到比较完之后第四部分没有元素。
4.从第四部分选出一个元素a[K],与两个轴心比较,然后放到第一二三部分中的一个。
5.移动L,K,G指向。
6.重复 4 5 步,直到第四部分没有元素。
7.将P1与第一部分的最后一个元素交换。将P2与第三部分的第一个元素交换。
8.递归的将第一二三部分排序。

图表演示

注:图片来自Vladimir Yaroslavskiy的论文。

算法源码

  1. //对外公开的两个sort方法
  2. public static void sort(int[] a) {
  3. sort(a, 0, a.length);
  4. }
  5. public static void sort(int[] a, int fromIndex, int toIndex) {
  6. rangeCheck(a.length, fromIndex, toIndex);
  7. dualPivotQuicksort(a, fromIndex, toIndex - 1, 3);
  8. }
  9. //对数组的边界检测
  10. private static void rangeCheck(int length, int fromIndex, int toIndex) {
  11. if (fromIndex > toIndex) {
  12. throw new IllegalArgumentException("fromIndex > toIndex");
  13. }
  14. if (fromIndex < 0) {
  15. throw new ArrayIndexOutOfBoundsException(fromIndex);
  16. }
  17. if (toIndex > length) {
  18. throw new ArrayIndexOutOfBoundsException(toIndex);
  19. }
  20. }
  21. //交换数组中两个元素
  22. private static void swap(int[] a, int i, int j) {
  23. int temp = a[i];
  24. a[i] = a[j];
  25. a[j] = temp;
  26. }
  27. /**
  28. * 双轴快排的具体实现
  29. * @param a     待排序数组
  30. * @param left  数组需排序上界
  31. * @param right 数组需排序下界
  32. * @param div   理解为从何位置取轴
  33. */
  34. private static void dualPivotQuicksort(int[] a, int left,int right, int div) {
  35. int len = right - left;
  36. //数组长度如果很小(27),则直接用插入排序对其排序
  37. if (len < 27) {
  38. for (int i = left + 1; i <= right; i++) {
  39. for (int j = i; j > left && a[j] < a[j - 1]; j--) {
  40. swap(a, j, j - 1);
  41. }
  42. }
  43. return;
  44. }
  45. //取到位于1/div和div-1/div位置的点,并用他们来做轴
  46. int third = len / div;
  47. int m1 = left + third;
  48. int m2 = right - third;
  49. if (m1 <= left) {
  50. m1 = left + 1;
  51. }
  52. if (m2 >= right) {
  53. m2 = right - 1;
  54. }
  55. //确保left是小的,right是大的
  56. if (a[m1] < a[m2]) {
  57. swap(a, m1, left);
  58. swap(a, m2, right);
  59. }
  60. else {
  61. swap(a, m1, right);
  62. swap(a, m2, left);
  63. }
  64. // 两个轴
  65. int pivot1 = a[left];
  66. int pivot2 = a[right];
  67. // 代表比p1小和比p2大的两个指针
  68. int less = left + 1;
  69. int great = right - 1;
  70. // 开始取出less到great之间的未知大小数据,与两个轴比较
  71. // 并且将数据放入正确的区域后调整各个指针
  72. for (int k = less; k <= great; k++) {
  73. //如果取出的数比p1小,那么直接到less左侧,并且less右移
  74. if (a[k] < pivot1) {
  75. swap(a, k, less++);
  76. }
  77. //如果取出的数比p2大,那么首先确定great左侧没有比p2大的数
  78. //然后与great位置的数字交换,great左移
  79. //此时,great交换的数字肯定是比p2小或者相等的(首先确定过)
  80. //那么此时再与p1相比,处理这个数的区间
  81. else if (a[k] > pivot2) {
  82. while (k < great && a[great] > pivot2) {
  83. great--;
  84. }
  85. swap(a, k, great--);
  86. if (a[k] < pivot1) {
  87. swap(a, k, less++);
  88. }
  89. }
  90. //如果这个数比p1大但是比p2小,则不需要交换,只需将k指针右移
  91. }
  92. //将p1与less左侧的第一个数交换
  93. swap(a, less - 1, left);
  94. //将p2与great右侧的第一个数交换
  95. swap(a, great + 1, right);
  96. // 计算出在两轴大小之间的个数
  97. int dist = great - less;
  98. //如果这个数很小(13),那么取轴的点向两边偏
  99. if (dist < 13) {
  100. div++;
  101. }
  102. // 对三个子区间分别排序,因为less-1和great+1是轴,已经排好了序
  103. // 所以不需要比较
  104. dualPivotQuicksort(a, left, less - 2, div);
  105. dualPivotQuicksort(a, great + 2, right, div);
  106. // 如果在中间区间的数字很多,那么排除掉一些相等的元素再进行排序
  107. if (dist > len - 13 && pivot1 != pivot2) {
  108. for (int k = less; k <= great; k++) {
  109. if (a[k] == pivot1) {
  110. swap(a, k, less++);
  111. }
  112. else if (a[k] == pivot2) {
  113. swap(a, k, great--);
  114. if (a[k] == pivot1) {
  115. swap(a, k, less++);
  116. }
  117. }
  118. }
  119. }
  120. // 对中间的区间排序
  121. if (pivot1 < pivot2) {
  122. dualPivotQuicksort(a, less, great, div);
  123. }
  124. }

总结

双轴排序利用了区间相邻的特性,对原本的快排进行了效率上的提高,很大程度上是利用了数学的一些特性,果然,算法跟数学还是息息相关的吖。

jdk1.6与jdk1.7list集合排序区别与算法的更多相关文章

  1. 记一次诡异的bug调试——————关于JDK1.7和JDK1.8中HashSet的hash(key)算法的区别

    现象: 测试提了一个bug,我完全复现不了,但是最吊诡的是在其他人的机器上都可以复现.起初以为是SVN合并后出现的冲突,后来经过对比法排查: step 1: 我本地开两个jetty,一个跑合并之前的版 ...

  2. 集合排序 Comparator和Comparable的使用区别

    Java 排序 Compare  Comparator接口 Comparable接口 区别 在Java中使用集合来存储数据时非常常见的,集合排序功能也是常用功能之一.下面看一下如何进行集合排序,常用的 ...

  3. Jdk1.7 与 jdk1.8的区别,最新的特征有哪些(美团,360,京东面试题目)

    在jdk7的新特性方面主要有下面几方面的增强: 1.1二进制变量的表示,支持将整数类型用二进制来表示,用0b开头. 所有整数int.short.long.byte都可以用二进制表示: byte aBy ...

  4. java经常看见 jdk5 jdk1.5 —— jdk6 jdk1.6 这两者有什么区别吗?

    问.java经常看见 jdk5 jdk1.5 —— jdk6 jdk1.6 这两者有什么区别吗? 答:没有区别,jdk5 和 jdk1.5 所代表的意思是一样的,只是叫法不一样 关键字: jdk5 j ...

  5. 集合排序Comparable和Comparator有什么区别?

    Comparable和Comparator兄弟俩长得是真像.但是,需要注意下,使用中它们还是有不少区别的.下面,就一探究竟吧. 一.Comparator 做过集合排序的童鞋应该知道,可以使用Colle ...

  6. 牛客网Java刷题知识点之HashMap的实现原理、HashMap的存储结构、HashMap在JDK1.6、JDK1.7、JDK1.8之间的差异以及带来的性能影响

    不多说,直接上干货! 福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号:   大数据躺过的坑      Java从入门到架构师      人工智能躺过的坑          ...

  7. CopyOnWriteArrayList集合排序异常问题

    1.集合自定义排序实现 对List集合的自定义排序想必大家都知道要使用如下的方式,通过实现Comparator接口并实现compare方法来实现. /** * * @方法名 changeChain * ...

  8. hashMap在jdk1.7与jdk1.8中的原理及不同

    在分析jdk1.7中HashMap的hash冲突时,不知大家是否有个疑问就是万一发生碰撞的节点非常多怎么版?如果说成百上千个节点在hash时发生碰撞,存储一个链表中,那么如果要查找其中一个节点,那就不 ...

  9. Java集合排序及java集合类详解--(Collection, List, Set, Map)

    1         集合框架 1.1         集合框架概述 1.1.1         容器简介 到目前为止,我们已经学习了如何创建多个不同的对象,定义了这些对象以后,我们就可以利用它们来做一 ...

随机推荐

  1. POJ1195Mobile phones (从二维树状数组到cdq分治)

    Suppose that the fourth generation mobile phone base stations in the Tampere area operate as follows ...

  2. Qt容器组件(一)之QGroupBox、QScrollArea、QToolBox、QTabWidget

    QT中有九种容器组件,分别是组合框QGroupBox.滚动区QScrollArea.工具箱QToolBox.选项卡QTabWidget.控件栈QWidgetStack.框架QFrame.组件QWidg ...

  3. AtCoder Grand Contest #026 C - String Coloring

    Time Limit: 3 sec / Memory Limit: 1024 MB Score : 600600 points Problem Statement You are given a st ...

  4. 【Lintcode】062.Search in Rotated Sorted Array

    题目: Suppose a sorted array is rotated at some pivot unknown to you beforehand. (i.e., 0 1 2 4 5 6 7  ...

  5. 九 Vue学习 manager页面布局

    1:  登录后系统页面如下: 对应代码: <template> <div class="manage_page fillcontain"> <el-r ...

  6. JSON 生成 C# Model

    http://www.cnblogs.com/tianqiq/p/4309791.html

  7. 2014 SCAU_ACM 暑期集训

    暑期集训,希望能在这段时间获得对得起自己的提升吧 时间:7.11~8.30 集训各专题内容: 1.贪心,递推,基础DP(背包,区间DP,状态压缩DP(去年出了不少于2道铜牌题,看着办)) 2.搜索(B ...

  8. shader之顶点着色器

    Vertex Shader 是渲染管道中一个可编程的模块,用于处理独立的顶点.Vertex Shader接收Vertex Attribute Data,由定点数组对象通过渲染指令来生成. Vertex ...

  9. TypeScript完全解读(26课时)_汇总贴

    ECMAScript 6 入门:http://es6.ruanyifeng.com/ 官网:http://www.typescriptlang.org/ 中文网:https://www.tslang. ...

  10. AngularJS 2.0 学习

    前提: 下载和安装node.js 和 npm. https://nodejs.org/en/download/ npm安装 需要自己google 安装好之后,可以在cmd窗口中查看安装的版本 node ...