TimeSort的sort部分源码

今天进行开发时,需要将集合进行排序。 走代码时,看到的 TimeSort 类型。
通过 ArrayList的 sort() 方法进入到 TimSort的Sort() 方法,记录部分逻辑。

a – 数组
lo – 数组第一个元素下角标
hi – 数组最后一个元素下角标 (值+1

    static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
                         T[] work, int workBase, int workLen) {
	//此处断言,处理入参不和规的情况
        assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;
	//  ho 排序从哪个index之开始,hi 到哪结束
        int nRemaining  = hi - lo; // 一共有几个
        if (nRemaining < 2)
            return;  // Arrays of size 0 and 1 are always sorted 

        // If array is small, do a "mini-TimSort" with no merges
	// MIN_MERGE 为32 ,也就说,参与排序的元素数量小于32个,不使用 分合方式 进行排序
        if (nRemaining < MIN_MERGE) {
	// 返回一个值,看 ② countRunAndMakeAscending 方法
            int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
	// 进行排序
	// 如果 c == null 按照二进制排序
	// 如果 c != null 按照 c 进行排序
	// 看 ③ binarySort 方法
	// 可以看做 从 (lo + initRunLen)位置开始,进行位置调整,直到 hi 的位置
            binarySort(a, lo, hi, lo + initRunLen, c);
            return;
        }
	// 以上代码为 元素数量 < 32,情况下所走逻辑
        /**
         * 如果元素数量太多,通过 分合方式 进行排序
         */
        TimSort<T> ts = new TimSort<>(a, c, work, workBase, workLen);
	// 获取 单个区不超过32为的最大中间值
        int minRun = minRunLength(nRemaining);
        do {
            // 返回一个值,看 ② countRunAndMakeAscending 方法。runLen 排序正确的数量
            int runLen = countRunAndMakeAscending(a, lo, hi, c);

            // runLen < minRun 表示第一个排序区间的内容并不是排序正确的
            if (runLen < minRun) {
                int force = nRemaining <= minRun ? nRemaining : minRun;
		// 看 ③ binarySort 方法
		// 可以看做 从 (lo + runLen)位置开始,进行位置调整,直到 (lo + force) 的位置
                binarySort(a, lo, lo + force, lo + runLen, c);
		// 此时排序正确的数值为 force 个
                runLen = force;
            }

            // Push run onto pending-run stack, and maybe merge
		// 进行代码合并,分段进行合并,以下不做细看
            ts.pushRun(lo, runLen);
            ts.mergeCollapse();

            // Advance to find next run
            lo += runLen;
            nRemaining -= runLen;
        } while (nRemaining != 0);

        // Merge all remaining runs to complete sort
        assert lo == hi;
        ts.mergeForceCollapse();
        assert ts.stackSize == 1;
    }
② countRunAndMakeAscending 方法,返回initRunLen 用于确认现数组中有几个值是排序正确的
private static <T> int countRunAndMakeAscending(T[] a, int lo, int hi,
                                                    Comparator<? super T> c) {
        assert lo < hi;
        int runHi = lo + 1;
        if (runHi == hi)
            return 1;

        // Find end of run, and reverse range if descending
		// 通过 前两个元素 来判断数组按照正序是倒序
        if (c.compare(a[runHi++], a[lo]) < 0) { // Descending
            while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)
                runHi++;
		// 进行倒序,是将数值两边的值位置进行调换
            reverseRange(a, lo, runHi);
        } else {                              // Ascending
            while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)
                runHi++;
        }
	// 上面方法的作用:
	// ① 判断数组是按正序还是倒序,如果是倒序,进行位置调整,如果是正序,不进行位置调整
	// ② 确认当前数组排序正常的位置
	// runHi 为 排序正常的最后一个元素下角标
	// runHi - lo 为 第一个值
        return runHi - lo;
    }
③ binarySort 方法,通过二分法确定元素位置,进行位置替换,从而达到排序的效果
private static <T> void binarySort(T[] a, int lo, int hi, int start,
                                       Comparator<? super T> c) {
	// start 为 ② 返回的 intRunLen,排序的开端
        assert lo <= start && start <= hi;
        if (start == lo)
            start++;
        for ( ; start < hi; start++) {
            T pivot = a[start];

            // Set left (and right) to the index where a[start] (pivot) belongs
            int left = lo;
            int right = start;
            assert left <= right;
            /*
             * Invariants: 期望值
             *   pivot >= all in [lo, left).
             *   pivot <  all in [right, start).
             */
            while (left < right) {
                int mid = (left + right) >>> 1;
                if (c.compare(pivot, a[mid]) < 0)
                    right = mid;
                else
                    left = mid + 1;
            }
	// 以上代码的作用是 通过二分法的方式确认pivot 应该与那个值进行互换
	// assert 当 left == right 可以确认去位置
            assert left == right;

            /*
             * The invariants still hold: pivot >= all in [lo, left) and
             * pivot < all in [left, start), so pivot belongs at left.  Note
             * that if there are elements equal to pivot, left points to the
             * first slot after them -- that's why this sort is stable.
             * Slide elements over to make room for pivot.
             */
	// The number of elements to move
	// n 为 pivot 与 互换元素 之间的位置,通过距离超过2位,通过 复制数组实现
            int n = start - left;
            // Switch is just an optimization for arraycopy in default case
            switch (n) {
                case 2:  a[left + 2] = a[left + 1];
                case 1:  a[left + 1] = a[left];
                         break;
                default: System.arraycopy(a, left, a, left + 1, n);
            }
            a[left] = pivot;
        }
posted @ 2023-05-14 16:31  之士咖啡  阅读(51)  评论(0编辑  收藏  举报