JDK8中 Arrays.sort 底层排序算法的选择

本文详细剖析了Java中Arrays.sort()方法的实现细节,包括其结合插入排序、快速排序和归并排序的混合策略。当元素数量小于47时使用插入排序,小于286时采用快速排序,而大于286时,先检查数组结构,如果具备一定结构则使用归并排序,否则仍用快速排序。整个过程体现了算法的高效性和灵活性。
摘要由CSDN通过智能技术生成

追到底层源码分析一下:

// Use Quicksort on small arrays
if (right - left < QUICKSORT_THRESHOLD)
{
       //QUICKSORT_THRESHOLD = 286
        sort(a, left, right, true);
        return;
 }

数组一进来,会碰到第一个阀值QUICKSORT_THRESHOLD(=286),注解上说,小于这个阀值的进入Quicksort (快速排序),其实并不全是,点进去sort(a, left, right, true)方法:

// Use insertion sort on tiny arrays
if (length < INSERTION_SORT_THRESHOLD)
{
    if (leftmost)
    {
        ......

点进去后我们看到第二个阀值INSERTION_SORT_THRESHOLD(=47),如果元素个数少于47这个阀值,就用插入排序,往下看确实如此:

/*
 * 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;
}

由此可知,元素个数少于47用插入排序

插入排序:
【插入排序属于内部排序,是对于待排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的.】

排序思想:
把 n 个待排序的元素看成一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有 n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。

插入排序 动态图解:
在这里插入图片描述

至于 大过INSERTION_SORT_THRESHOLD(=47)的,用 快速排序方法
【快速排序是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分发所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列】

  1. 从数列中挑出五个元素,称为 “基准”(pivot);

  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;

  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
    举例分析:
    在这里插入图片描述
    另一个例子 动态分析图:
    在这里插入图片描述

以上是少于阀值QUICKSORT_THRESHOLD(=286)的两种情况。

至于 大于286的,它会进入归并排序(Merge Sort),但在此之前,它有个小动作:

// Check if the array is nearly sorted
    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;
        }
    }

这里主要作用是看他数组具不具备结构:实际逻辑是分组排序,每降序为一个组,像1,9,8,7,6,8。9到6是降序,为一个组,然后把降序的一组排成升序:1,6,7,8,9,8。然后最后的8后面继续往后面找。

每遇到这样一个降序组,++count,当count大于MAX_RUN_COUNT(67),被判断为这个数组不具备结构(也就是这数据时而升时而降),然后送给之前的sort(里面的快速排序)的方法(The array is not highly structured,use Quicksort instead of merge sort.)

如果count少于MAX_RUN_COUNT(67)的,说明这个数组还有点结构,就继续往下走下面的归并排序。

归并排序:

  • 是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分成一些小的问题,然后递归求解,而 治 的阶段则将分的阶段得到的各答案“修补”在一起,即分而治之)

动态图解:
在这里插入图片描述

总结:

从上面分析,Arrays.sort并不是单一的排序,而是插入排序,快速排序,归并排序三种排序的组合,为此我画了个流程图:
在这里插入图片描述
关于算法的稳定性:

稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;

不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;

时间复杂度按n越大算法越复杂来排的话:常数阶O(1)、对数阶O(logn)、线性阶O(n)、线性对数阶O(nlogn)、平方阶O(n²)、立方阶O(n³)、……k次方阶O(n的k次方)、指数阶O(2的n次方)。

  • O(nlogn)只代表增长量级,同一个量级前面的常数也可以不一样,不同数量下面的实际运算时间也可以不一样。

  • 数量非常小的情况下(就像上面说到的,少于47的),插入排序等可能会比快速排序更快。 所以数组少于47的会进入插入排序。

    快排数据越无序越快(加入随机化后基本不会退化),平均常数最小,不需要额外空间,不稳定排序。
    归排速度稳定,常数比快排略大,需要额外空间,稳定排序。

所以大于或等于47或少于286会进入快排,
而在大于或等于286后,会有个小动作:“// Check if the array is nearly sorted”。这里第一个作用是先梳理一下数据方便后续的归并排序,第二个作用就是即便大于286,
但在降序组太多的时候(被判断为没有结构的数据,The array is not highly structured,use Quicksort instead of merge sort.),要转回快速排序。

参考文章:Java的Arrays.sort()方法到底用的什么排序算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值