JDK source study notes ~ Arrays.sort ()

Recently brush on LeetCode problem, just having trouble sorting an array of found using JDK comes Arrays.sort () method is faster than most of the sort common sorting algorithm, so I quickly came to understand learning under Arrays.sort () exactly how to achieve the underlying ordered.

Specific JDK source code to build reading environment can refer to my another blog JDK source code reading environment to build (make an ad ؏؏☝ᖗ ◡ Ya Ya ᖘ☝؏؏)

(Ps: If you can not find the small partners tools package to see whether it had time to install the jdk jdk and jre path is modified to the same path, and if it is the same paths, then congratulations, you need to uninstall reinstall JDK, because the packets are overwritten, so the package will be reported less wrong)

Entrance Case

package test.arrays;

import java.util.Arrays;

public class TestArrays {
    private static Random r = new Random();
    
    public static void main(String[] args) {
        // 初始化数组
        int[] arr = new int[286];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = r.nextInt(100);
        }
		// 入口
        Arrays.sort(arr);
    }
}

Arrays.sort()

    /*
     * 排序方法。
     * 请注意,所有公共“ sort”方法都采用相同的形式:
     * 必要时执行参数检查,然后将参数扩展为其他package-private类中内部的实现方法所需的参数(legacyMergeSort除外)类)
     */

    /**
     * 将指定的数组按升序排列。
     * <p>实施说明:
     * 排序算法是Vladimir Yaroslavskiy,Jon Bentley和Joshua Bloch编写的双枢轴快速排序。
     * 该算法在许多数据集上提供O(n log(n))性能,从而导致其他快速排序降级为二次性能,并且通常比传统(单轴)Quicksort实现更快。
     * @param a 一个要排序的数组
     */
    public static void sort(int[] a) {
        DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
    }

In Arrays.sort () , we can see that there are using the Dual-Pivot Quicksort to sort, continue to jump to DualPivotQuicksort class View

DualPivotQuicksort.sort()

    /**
     * 如果可能的话,使用给定的工作区数组切片对数组的指定范围进行排序
     * @param a 要排序的数组
     * @param left 要排序的第一个元素的索引(含)
     * @param right 要排序的最后一个元素的索引(含)
     * @param work 工作区数组(切片)
     * @param work 工作阵列中可用空间的起源
     * @param workLen 工作数组的可用大小
     */
    static void sort(int[] a, int left, int right,
                     int[] work, int workBase, int workLen) {
        // 在小型阵列上使用快速排序
        // QUICKSORT_THRESHOLD:  如果要排序的数组的长度小于此常数,则快速排序优先于合并排序。(默认值: 286)
        if (right - left < QUICKSORT_THRESHOLD) {
            sort(a, left, right, true);
            return;
        }

        /*
         * 索引 run[i] 是第i次运行的开始(升序或降序)。
         * MAX_RUN_COUNT: 合并排序中的最大运行次数(默认值: 67)
         */
        int[] run = new int[MAX_RUN_COUNT + 1];
        int count = 0; run[0] = left;

        // 检查数组是否接近排序
        for (int k = left; k < right; run[count] = k) {
            if (a[k] < a[k + 1]) { // 升序
                while (++k <= right && a[k - 1] <= a[k]);
            } else if (a[k] > a[k + 1]) { // 降序
                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 { // 等于
                // MAX_RUN_LENGTH: 合并排序中运行的最大长度(默认值: 33)
                for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) {
                    if (--m == 0) {
                        sort(a, left, right, true);
                        return;
                    }
                }
            }

            /*
             * 数组不是高度结构化,请使用快速排序而不是合并排序。
             */
            if (++count == MAX_RUN_COUNT) {
                sort(a, left, right, true);
                return;
            }
        }

        // 后面还有特殊情况,不知怎么测试出来就不贴了...
    }

Will first be sorted array length determination process is performed, if the length is less than 286 , then using the Dual-the Pivot Quicksort (double pivot quicksort)

Dual-Pivot Quicksort

    /**
     * 通过Dual-Pivot Quicksort对指定范围的数组进行排序。
     *
     * @param a 要排序的数组
     * @param left 要排序的第一个元素的索引(含)
     * @param right 要排序的最后一个元素的索引(含)
     * @param leftmost 指示此部分是否在范围的最左侧
     */
    private static void sort(int[] a, int left, int right, boolean leftmost) {
        int length = right - left + 1;

        // 在小型阵列上使用插入排序
        // INSERTION_SORT_THRESHOLD: 如果要排序的数组的长度小于此常数,插入排序优先于快速排序使用。
        if (length < INSERTION_SORT_THRESHOLD) {
            // 为了方便查看,我把这部分单独拿了出来,可调到下面进行查看
            return;
        }

        // 得到总长度的七分之一
        int seventh = (length >> 3) + (length >> 6) + 1;

        /*
         * 在范围内的中心元素周围(包括周围)对五个等距元素进行排序。
         * 这些元素将用于枢轴选择,如下所述。
         * 根据经验确定这些元素的间距选择可以在各种输入上很好地工作。
         */
        int e3 = (left + right) >>> 1; // 中点, 4/7
        int e2 = e3 - seventh; // 3/7
        int e1 = e2 - seventh; // 2/7
        int e4 = e3 + seventh; // 5/7
        int e5 = e4 + seventh; // 6/7

        // 使用插入排序对五个元素(枢轴)进行排序
        // 第二个点小于第一个点则交换
        if (a[e2] < a[e1]) { int t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
		// 第三个点小于第二个点则交换
        if (a[e3] < a[e2]) { int t = a[e3]; a[e3] = a[e2]; a[e2] = t;
            // 3换到2之后再判断是否小于1,如果小于1则继续交换
            if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
        }
        // 同上
        if (a[e4] < a[e3]) { int t = a[e4]; a[e4] = a[e3]; a[e3] = t;
            if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
                if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
            }
        }
        // 同上
        if (a[e5] < a[e4]) { int t = a[e5]; a[e5] = a[e4]; a[e4] = t;
            if (t < a[e3]) { a[e4] = a[e3]; a[e3] = t;
                if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
                    if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
                }
            }
        }

        // 指针
        int less  = left;
        int great = right;
		// 判断五个元素是否都不一致
        if (a[e1] != a[e2] && a[e2] != a[e3] && a[e3] != a[e4] && a[e4] != a[e5]) {
            /*
             * 使用五个排序元素中的第二个和第四个作为轴心。
             * 这些值是阵列的第一和第二对的便宜的近似值。
             * 请注意 pivot1 <= pivot2。
             */
            int pivot1 = a[e2];
            int pivot2 = a[e4];

            /*
             * 将要排序的第一个和最后一个元素移动到以前由枢轴占据的位置。
             * 分区完成后,枢轴将交换回其最终位置,并从后续排序中排除。
             */
            a[e2] = a[left];
            a[e4] = a[right];

            /*
             * 跳过元素, 小于或大于枢轴值。
             */
            while (a[++less] < pivot1);
            while (a[--great] > pivot2);

            /*
             * 分区:
             *
             *   left part           center part                   right part
             * +--------------------------------------------------------------+
             * |  < pivot1  |  pivot1 <= && <= pivot2  |    ?    |  > pivot2  |
             * +--------------------------------------------------------------+
             *               ^                          ^       ^
             *               |                          |       |
             *              less                        k     great
             *
             * Invariants:
             *
             *              all in (left, less)   < pivot1
             *    pivot1 <= all in [less, k)     <= pivot2
             *              all in (great, right) > pivot2
             *
             * 指针 k 是 ?至part 的第一个索引.
             */
            outer:
            for (int k = less - 1; ++k <= great; ) {
                int ak = a[k];
                if (ak < pivot1) { // 移动 a[k] 到左边
                    a[k] = a[less];
                    /*
                     * 由于性能问题,在这里和下面,
                     * 我们使用 "a[i] = b; i++;" 代替 "a[i++] = b;" 
                     */
                    a[less] = ak;
                    ++less;
                } else if (ak > pivot2) { // 移动 a[k] 到右边
                    // 获取右边第一位小于pivot2的元素索引
                    while (a[great] > pivot2) {
                        if (great-- == k) { // 遍历完跳出循环
                            break outer;
                        }
                    }
                    if (a[great] < pivot1) { // a[great] <= pivot2
                        a[k] = a[less];
                        a[less] = a[great];
                        ++less;
                    } else { // pivot1 <= a[great] <= pivot2
                        a[k] = a[great];
                    }
                    /*
                     * 由于性能问题,在这里和下面, 
                     * 我们使用 "a[i] = b; i--;" 代替 "a[i--] = b;"
                     */
                    a[great] = ak;
                    --great;
                }
            }

            // 交换枢轴到最终位置
            a[left]  = a[less  - 1]; a[less  - 1] = pivot1;
            a[right] = a[great + 1]; a[great + 1] = pivot2;

            // 递归排序左右部分,不包括已知的轴
            sort(a, left, less - 2, leftmost);
            sort(a, great + 2, right, false);

            /*
             * 如果中心部分太大(大于等于数组的七分之四),则将内部枢轴值交换到末端。
             */
            if (less < e1 && e5 < great) {
                /*
                 * 跳过等于枢轴值的元素。
                 */
                while (a[less] == pivot1) {
                    ++less;
                }

                while (a[great] == pivot2) {
                    --great;
                }

                /*
                 * Partitioning:
                 *
                 *   left part         center part                  right part
                 * +----------------------------------------------------------+
                 * | == pivot1 |  pivot1 < && < pivot2  |    ?    | == pivot2 |
                 * +----------------------------------------------------------+
                 *              ^                        ^       ^
                 *              |                        |       |
                 *             less                      k     great
                 *
                 * Invariants:
                 *
                 *              all in (*,  less) == pivot1
                 *     pivot1 < all in [less,  k)  < pivot2
                 *              all in (great, *) == pivot2
                 *
                 * Pointer k is the first index of ?-part.
                 */
                outer:
                for (int k = less - 1; ++k <= great; ) {
                    int ak = a[k];
                    if (ak == pivot1) { // Move a[k] to left part
                        a[k] = a[less];
                        a[less] = ak;
                        ++less;
                    } else if (ak == pivot2) { // Move a[k] to right part
                        while (a[great] == pivot2) {
                            if (great-- == k) {
                                break outer;
                            }
                        }
                        if (a[great] == pivot1) { // a[great] < pivot2
                            a[k] = a[less];
                            /*
                             * 即使a[great]等于pivot1,
                             * 分配a[less] = pivot1可能不正确,
                             * 如果a[great]和pivot1是不同符号的浮点零。
                             * 因此,在浮点和双精度排序方法中
                             * 我们必须使用更准确的赋值a[less] = a[great]。
                             */
                            a[less] = pivot1;
                            ++less;
                        } else { // pivot1 < a[great] < pivot2
                            a[k] = a[great];
                        }
                        a[great] = ak;
                        --great;
                    }
                }
            }

            // 递归排序中心部分
            sort(a, less, great, false);

        } else { // 用一个枢轴进行分区
            /*
             * 使用五个排序元素中的第三个作为枢轴。
             * 该值是中值的廉价近似值。
             */
            int pivot = a[e3];

            /*
             * 分区退化为传统的3向(或“荷兰国旗”)架构
             *
             *   left part    center part              right part
             * +-------------------------------------------------+
             * |  < pivot  |   == pivot   |     ?    |  > pivot  |
             * +-------------------------------------------------+
             *              ^              ^        ^
             *              |              |        |
             *             less            k      great
             *
             * Invariants:
             *
             *   all in (left, less)   < pivot
             *   all in [less, k)     == pivot
             *   all in (great, right) > pivot
             *
             * Pointer k is the first index of ?-part.
             */
            for (int k = less; k <= great; ++k) {
                if (a[k] == pivot) {
                    continue;
                }
                int ak = a[k];
                if (ak < pivot) { // Move a[k] to left part
                    a[k] = a[less];
                    a[less] = ak;
                    ++less;
                } else { // a[k] > pivot - Move a[k] to right part
                    while (a[great] > pivot) {
                        --great;
                    }
                    if (a[great] < pivot) { // a[great] <= pivot
                        a[k] = a[less];
                        a[less] = a[great];
                        ++less;
                    } else { // a[great] == pivot
                        a[k] = pivot;
                    }
                    a[great] = ak;
                    --great;
                }
            }

            /*
             * 递归排序左右部分。
             * 中心部分的所有元素均相等,因此已经排序。
             */
            sort(a, left, less - 1, leftmost);
            sort(a, great + 1, right, false);
        }
    }

Will first be sorted array length method is determined, if the length is less than 47 words, use Insertion the Sort (insertion sort), a length greater than or equal 47 then the array is divided into five regions of the same length , sorting recursively

Insertion Sort

private static void sort(int[] a, int left, int right, boolean leftmost) {
	if (leftmost) {
        /*
         * 传统(无前哨)插入类型,
         * 针对服务器VM进行了优化,
         * 用于最左边的部分.
         */
        for (int i = left, j = i; i < right; j = ++i) {
            int ai = a[i + 1];	// 得到下一位元素的值
            /*
             * 判断ai是否小于前面的元素值
             * 如果小于则将前面的值后移一位
             * 一直往前遍历交换到首位或者前面元素值小于等于ai为止
             */
            while (ai < a[j]) {
                a[j + 1] = a[j];
                if (j-- == left) {
                    break;
                }
            }
            // 将最终索引出的值改为ai,完成交换
            a[j + 1] = ai;
        }
    } else {
        /*
         * 跳过最长的升序.
         */
        do {
            if (left >= right) {
                return;
            }
        } while (a[++left] >= a[left - 1]);

        /*
         * 相邻部分的每个元素都扮演着哨兵的角色,
         * 因此,这使我们避免了每次迭代的左范围检查。
         * 此外,我们使用更优化的算法
         * 所谓的配对插入排序
         * (在Quicksort的背景下)这比传统的插入排序实现要快。
         */
        for (int k = left; ++left <= right; k = ++left) {
            long 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;
        }
        long last = a[right];

        while (last < a[--right]) {
            a[right + 1] = a[right];
        }
        a[right + 1] = last;
    }
}

The underlying original Arrays.sort () is an array of different lengths to use sorting methods corresponding to learn! But this understanding was still feeling a little muddle, there can be big brother gets advice, band with me ((( ((((((((っ • ω •) っ Σ (σ `• ω • ') σ off!

Guess you like

Origin www.cnblogs.com/unrecognized/p/12635664.html
Recommended