Arrays.sort 对数组的排序(基本数据类型)

Arrays提供了对基本数据类型、引用数据类型的排序功能。本文只讨论基本数据类型的数组排序。

上图是Arrays中的除了boolean类型的所有排序方法。下面以int[]为例进行展开。

点开sort()方法,会看到请求一下方法

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

 点击进入 DualPivotQuicksort.sort;我们会看到首先会进行了一次判断

    /**
     * Sorts the specified range of the array using the given
     * workspace array slice if possible for merging
     *
     * @param a the array to be sorted
     * @param left the index of the first element, inclusive, to be sorted
     * @param right the index of the last element, inclusive, to be sorted
     * @param work a workspace array (slice)
     * @param workBase origin of usable space in work array
     * @param workLen usable size of work array
     */ 
    static void sort(int[] a, int left, int right,
                     int[] work, int workBase, int workLen) {
        // Use Quicksort on small arrays
        if (right - left < QUICKSORT_THRESHOLD) {
            sort(a, left, right, true);
            return;
        }

        。。。此处省略。。。
    }

当数组长度小于QUICKSORT_THRESHOLD(286)时,会进入私有方法sort中进行排序后直接返回。否则往下。

而点击进入sort方法时,又会有一个判断

    /**
     * Sorts the specified range of the array by Dual-Pivot Quicksort.
     *
     * @param a the array to be sorted
     * @param left the index of the first element, inclusive, to be sorted
     * @param right the index of the last element, inclusive, to be sorted
     * @param leftmost indicates if this part is the leftmost in the range
     */
    private static void sort(int[] a, int left, int right, boolean leftmost) {
        int length = right - left + 1;

         // Use insertion sort on tiny arrays
        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.
                 */
                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 (int k = left; ++left <= right; k = ++left) {
                    int a1 = a[k], a2 = a[left];

                    if (a1 < a2) {
                        a2 = a1; a1 = a[left];
                    }
                    while (a1 < a[--k]) {
                        a[k + 2] = a[k];
                    }
                    a[++k + 1] = a1;

                    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;
        }
        。。。省略。。。
    }

1. 当数组长度小于INSERTION_SORT_THRESHOLD(47)时,这里会使用插入排序,这是我们在里面看到的第一个排序算法(这里需要说明的是,这里有个参数leftmost,初始进入的时候,leftmost=true,在递归调用此方法(sort)的时候,leftmost会有不同的传值)。

2. 当数组长度为INSERTION_SORT_THRESHOLDQUICKSORT_THRESHOLD(不包含)之间时,又用到的是另外的排序算法。我们从注释里面可以看到,叫做Dual-Pivot Quicksort。其实就是一种快速排序的实现方式,只不过这里使用了轴元素pivot1,pivot2,将数组分成三段进行递归。这是我们在里面看到的第二种排序算法

3. 当数组长度超过QUICKSORT_THRESHOLD时,我们在注释里面可以看到merging,也就是归并排序的影子。但是真的是直接就使用了归并排序了吗,接下来我们可以进行分析一下。

在接下来的代码中,首先出现的是以下一段代码:

             /*
         * Index run[i] is the start of i-th run
         * (ascending or descending sequence).
         */
        int[] run = new int[MAX_RUN_COUNT + 1];
        int count = 0; run[0] = left;

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

文中有一句注释“Check if the array is nearly sorted”,校验数组是否趋近于有序。如果不是(The array is not highly structured),那么就使用前面提到的私有sort方法,用快速排序替代归并排序(use Quicksort instead of merge sort.),排序完成直接返回。否则往下走。

这里是什么意思呢?

这里定义一个常量MAX_RUN_COUNT = 67,一个计数器int count = 0,一个长度为MAX_RUN_COUNT + 1的数组,令run[0] = left,然后从传入数组的最左侧left开始遍历, 若数组的前n个元素均为升序/降序排列, 而第n + 1个元素的升/降序发生了改变, 则将第n个元素的索引存入run[1], 同时++count, 此时count的值为1;以此类推...

如果整个数组遍历完成,并且count小于MAX_RUN_COUNT ,则认为数组是高度结构化的,就认为是趋近于有序的。

然后接着往下看。

        // Check special cases
        // Implementation note: variable "right" is increased by 1.
        if (run[count] == right++) { // The last run contains one element
            run[++count] = right;
        } else if (count == 1) { // The array is already sorted
            return;
        }

如果count == 1,则整个数组遍历完了,升序/降序序列都没有发生改变,则认为数组是有序的,直接返回,否则往下,就是最终的归并排序了

总结一下:

这里有三个常量:

QUICKSORT_THRESHOLD = 286

INSERTION_SORT_THRESHOLD = 47

MAX_RUN_COUNT = 67

1. 如果数组长度 < 47,则进行插入排序

2. 如果数组长度 ≥ 47 且 < 286,则进行快速排序(两个Pivot

3. 如果数组长度 ≥ 286,如果数组趋近于有序,使用步骤2的排序方法,否则使用归并排序

注: 具体步骤自行debug调试,本文只是个人观点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值