JDK源码——Arrays.sort()的实现

Posted Vegout

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK源码——Arrays.sort()的实现相关的知识,希望对你有一定的参考价值。

主要分为两种,一个是对基本数据类型数组的排序实现,一个是对对象类型数组的排序实现。对于基本数据类型数组的排序实现主要采用了插入排序、快速排序和归并排序相结合的排序方法,对象类型数组的排序主要采用了归并排序和插入排序相结合的方法。每种排序方法都进行了一定的改进。今天分析的主要是基本数据类型元素的排序实现,各种基本数据类型排序的实现大同小异,这里采取对Int[]排序的实现代码进行分析。
进入Arrays.sort(int[] a)

  public static void sort(int[] a) {
       DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
   }

可见,Arrays并没有自己来实现排序,而是委托给了DualPivotQuicksort类。进入上边的sort方法

        if (right - left < QUICKSORT_THRESHOLD) {
           sort(a, left, right, true);
           return;
       }

首先对数组长度进行了判断,如果小于这个阈值(默认是286),直接选择快速排序进行排序。
如果大于这个阈值呢?咱接着往下看

  for (int k = left; k < right; run[count] = k) {
           if (a[k] < a[k + 1]) { // ascending
               while (++k <= right && a[k - 1] <= a[k]);
           } else if (a[k] > a[k + 1]) { // descending
               while (++k <= right && a[k - 1] >= a[k]);
               for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {
                   int t = a[lo]; a[lo] = a[hi]; a[hi] = t;
               }
           } else { // equal
               for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) {
                   if (--m == 0) {
                       sort(a, left, right, true);
                       return;
                   }
               }
           }
           /*
            * The array is not highly structured,
            * use Quicksort instead of merge sort.
            */

           if (++count == MAX_RUN_COUNT) {
               sort(a, left, right, true);
               return;
           }
       }

进入了一个for循环,这个循环就是用来判断这个长数组是否基本有序,如果基本有序则选择归并排序,否则任然选择快排。如果它选择了快排,那么就会在这个for循环中的两个return处结束这次排序,如果选择了归并排序,则故事就在for循环之后继续发生(for循环之后的代码就是归并排序的实现)。

        for (int last; count > 1; count = last) {
           for (int k = (last = 0) + 2; k <= count; k += 2) {
               int hi = run[k], mi = run[k - 1];
               for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {
                   if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {
                       b[i + bo] = a[p++ + ao];
                   } else {
                       b[i + bo] = a[q++ + ao];
                   }
               }
               run[++last] = hi;
           }
           //如果a中子序列个数是奇数,则最后一个没有完成配对归并的序列直接复制到b中
           if ((count & 1) != 0) {
               for (int i = right, lo = run[count - 1]; --i >= lo;
                   b[i + bo] = a[i + ao]
               );
               run[++last] = right;
           }
           int[] t = a; a = b; b = t;
           int o = ao; ao = bo; bo = o;
       }

这个for循环是归并排序的主要部分,因为在上个for循环判断数组是否基本有序的时候,已经记录了原数组的各个有序的子序列,归并排序也就是对这些子序列进行自底向上的归并排序。
当然了如果选择了快排,快排的实现也进行了改进

        if (length < INSERTION_SORT_THRESHOLD) {
           if (leftmost) {
               /*
                * Traditional (without sentinel) insertion sort,
                * optimized for server VM, is used in case of
                * the leftmost part.
                */

               for (int i = left, j = i; i < right; j = ++i) {
                   int ai = a[i + 1];
                   while (ai < a[j]) {
                       a[j + 1] = a[j];
                       if (j-- == left) {
                           break;
                       }
                   }
                   a[j + 1] = ai;
               }
           } else {
               /*
                * Skip the longest ascending sequence.
                * 跳过最长的上升序列,比如数组a内容为:1245986,进过这步之后a[left]的内容将成为9,即Left=4                 */

               do {
                   if (left >= right) {
                       return;
                   }
               } while (a[++left] >= a[left - 1]);
               /*
                * Every element from adjoining part plays the role
                * of sentinel, therefore this allows us to avoid the
                * left range check on each iteration. Moreover, we use
                * the more optimized algorithm, so called pair insertion
                * sort, which is faster (in the context of Quicksort)
                * than traditional implementation of insertion sort.
                * 双插入排序,每次完成两个元素的插入排序。
                * 开始我还一直再纠结:for循环第三个条件为什么是k=++left,这样a1和a2岂不是一个元素了嘛?
                * 仔细一看红了脸,害羞.jpf
                */

               for (int k = left; ++left <= right; k = ++left) {
                   int a1 = a[k], a2 = a[left];
                   //保证a1>=a2
                   if (a1 < a2) {
                       a2 = a1; a1 = a[left];
                   }
                   //将a1进行插入
                   while (a1 < a[--k]) {
                       a[k + 2] = a[k];
                   }
                   a[++k + 1] = a1;
                   //将a2进行插入
                   while (a2 < a[--k]) {
                       a[k + 1] = a[k];
                   }
                   a[k + 1] = a2;
               }
               //如果元素个数为奇数,最后一个元素没有进行配对需要另外进行插入排序
               int last = a[right];
               while (last < a[--right]) {
                   a[right + 1] = a[right];
               }
               a[right + 1] = last;
           }
           return;
       }

进入快排就一定快排吗?那是不可能滴!首先还是进行数组长度的判断,如果小于一定阈值(默认是47),则进行插入排序,大家仔细看一下上边的代码,跟我们平常的插入排序有所不同,他每个轮次会完成两个元素的插入,官方称这种为pair insertion sort。
如果数组没这么小,那就会进行真正的快排了。这个快排被称为双轴快速排序,主要还是快排的思想,以及三项切分快速排序的升华。采用两个轴进行快排,使得整个数组分为三个部分,左边部分的元素都小于第一个轴,中间的部分大于等于第一个轴小于等于第二个轴,右边部分都大于第二个轴。然后对这三个部分递归调用双轴快排,当然还有许多细节的优化,比如如果中间部分过大则会把和轴相等的元素移动到两边,如果重复元素过多还会考虑三项切分快速排序等。

可能需要的链接



   如果您喜欢这篇文章

期待您的关注



以上是关于JDK源码——Arrays.sort()的实现的主要内容,如果未能解决你的问题,请参考以下文章

Arrays.sort源码解析

jdk8源码Arrays.sort插入排序,居然还可以成对插入

jdk8源码Arrays.sort插入排序,居然还可以成对插入

jdk8源码Arrays.sort插入排序

Arrays.sort解析

jdk8源码TimSort算法——从头看到脚