Collections 的sort源码阅读(对比1.7之前和1.7之后sort做了哪些优化)

jdk1.7 和 jdk1.8做对比阅读

jdk1.7

public static <T extends Comparable<? super T>> void sort(List<T> var0) {
    Object[] var1 = var0.toArray();
    //对var1排序后
    Arrays.sort(var1);
    ListIterator var2 = var0.listIterator();
    //重新覆盖到 var0
    for(int var3 = 0; var3 < var1.length; ++var3) {
        var2.next();
        var2.set((Comparable)var1[var3]);
    }

}

public static <T> void sort(List<T> var0, Comparator<? super T> var1) {
    Object[] var2 = var0.toArray();
    Arrays.sort(var2, var1);
    ListIterator var3 = var0.listIterator();

    for(int var4 = 0; var4 < var2.length; ++var4) {
        var3.next();
        var3.set(var2[var4]);
    }

}

jdk1.8

public static <T extends Comparable<? super T>> void sort(List<T> list) {
    list.sort(null);
}
default void sort(Comparator<? super E> c) {
    Object[] a = this.toArray();
    Arrays.sort(a, (Comparator) c);
    ListIterator<E> i = this.listIterator();
    for (Object e : a) {
        i.next();
        i.set((E) e);
    }
}

最终都是走到了 Arrays.sort方法

public static <T> void sort(T[] a, Comparator<? super T> c) {
    if (c == null) {
        sort(a);
    } else {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a, c);
        else
            TimSort.sort(a, 0, a.length, c, null, 0, 0);
    }
}

这里注意 LegacyMergeSort.userRequested  ,读取的java.util.Arrays.useLegacyMergeSort 用来兼容历史排序方法,比如1.7版本的jdk想使用1.6的排序方法,就需要配置这个参数  java.util.Arrays.useLegacyMergeSort=true

static final class LegacyMergeSort {
    private static final boolean userRequested =
        java.security.AccessController.doPrivileged(
            new sun.security.action.GetBooleanAction(
                "java.util.Arrays.useLegacyMergeSort")).booleanValue();
}

接下来先看一下1.7以前使用的哪种排序办法

private static void mergeSort(Object[] src,
                              Object[] dest,
                              int low,
                              int high,
                              int off) {
    int length = high - low;

    // 这里是一个优化点,如果是一个比较小的数组 直接使用插入排序
    if (length < INSERTIONSORT_THRESHOLD) {//这里默认是 7数组长度小于7
        for (int i=low; i<high; i++)
            for (int j=i; j>low &&
                     ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
                swap(dest, j, j-1);
        return;
    }

    // Recursively sort halves of dest into src
    int destLow  = low;
    int destHigh = high;
    low  += off;
    high += off;
    int mid = (low + high) >>> 1;//二进制右移一位相当于除以2
    mergeSort(dest, src, low, mid, -off);
    mergeSort(dest, src, mid, high, -off);

    // If list is already sorted, just copy from src to dest.  This is an
    // optimization that results in faster sorts for nearly ordered lists.
    if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) {//如果两部分分别排序后,左侧的最大 小于右侧最小 说明整体 src已经有序 直接复制到 dest
        System.arraycopy(src, low, dest, destLow, length);
        return;
    }

    // 
    for(int i = destLow, p = low, q = mid; i < destHigh; i++) {//分别遍历左侧 和右侧 合并到dest,需要考虑到某一个先遍历到头后的情景 q>=high 和 p<mid
        if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0)
            dest[i] = src[p++];
        else
            dest[i] = src[q++];
    }
}

/**
 * Swaps x[a] with x[b].
 */
private static void swap(Object[] x, int a, int b) {
    Object t = x[a];
    x[a] = x[b];
    x[b] = t;
}

综合下来就是 归并+插入排序完成的。

接下来看1.7 和1.8的实现,他们的变化不变,这里就只贴1.8了

根据是否有 Comparator 参数分别为,带Comparator参数

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;

    int nRemaining  = hi - lo;
    if (nRemaining < 2)//长度小于2 直接返回
        return;  // Arrays of size 0 and 1 are always sorted

    // If array is small, do a "mini-TimSort" with no merges
    if (nRemaining < MIN_MERGE) {//长度小于32 直接返回
        int initRunLen = countRunAndMakeAscending(a, lo, hi, c);//获取有序段,即lo 到几是有序的(如果是从大到小有序的话,会对这一小段进行反转)
        //执行到这里,0,到initRunLen 是有序的了
        binarySort(a, lo, hi, lo + initRunLen, c);//二分插入排序,就是把initRunLen位置后面的数据,通过二分搜索定位的方式插入到前面有序段落里面
        // 这里想吐槽一下  和 1.7之前的直接对小数组直接进行插入排序 感觉复杂了好多,不过确实复杂度变小了 从O(n^2) 变为了 O(nlogn)
        return;
    }

    /**
     * March over the array once, left to right, finding natural runs,
     * extending short natural runs to minRun elements, and merging runs
     * to maintain stack invariant.
     */
    TimSort<T> ts = new TimSort<>(a, c, work, workBase, workLen);
    int minRun = minRunLength(nRemaining);//获取有序run块的最小长度
    do {
        // Identify next run
        int runLen = countRunAndMakeAscending(a, lo, hi, c);//获取有序递增块的长度

        // If run is short, extend to min(minRun, nRemaining)
        if (runLen < minRun) {//如果长度过短,进行优化补充
            int force = nRemaining <= minRun ? nRemaining : minRun;
            binarySort(a, lo, lo + force, lo + runLen, c);
            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;
}
//用于获取一个递增连续区域长度
private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {
    assert lo < hi;
    int runHi = lo + 1;
    if (runHi == hi)
        return 1;

    // Find end of run, and reverse range if descending
    if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) { // Descending
        while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)
            runHi++;
        reverseRange(a, lo, runHi);
    } else {                              // Ascending
        while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) >= 0)
            runHi++;
    }

    return runHi - lo;
}
//二分插入排序
private static void binarySort(Object[] a, int lo, int hi, int start) {
    assert lo <= start && start <= hi;
    if (start == lo)
        start++;
    for ( ; start < hi; start++) {
        Comparable pivot = (Comparable) 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 (pivot.compareTo(a[mid]) < 0)
                right = mid;
            else
                left = mid + 1;
        }
        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.
         */
        int n = start - left;  // The number of elements to move
        // 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;
    }
}

接下来着重看一下最后的排序步骤

private void pushRun(int runBase, int runLen) {
    this.runBase[stackSize] = runBase;
    this.runLen[stackSize] = runLen;
    stackSize++;
}
private void mergeCollapse() {
    while (stackSize > 1) {
        int n = stackSize - 2;
        if (n > 0 && runLen[n-1] <= runLen[n] + runLen[n+1]) {
            if (runLen[n - 1] < runLen[n + 1])
                n--;
            mergeAt(n);
        } else if (runLen[n] <= runLen[n + 1]) {
            mergeAt(n);
        } else {
            break; // Invariant is established
        }
    }
}

总结,1.7之前的排序更易懂,插入+归并,数组长度低于7直接插入排序,

1.7开始使用TimSort+二分插入,数组长度小于32,直接二分插入排序

TimSort的平均时间复杂度和最坏时间复杂度和归并一样O(nlogn),但是其最好情况能达到O(n)

有理解错误的地方欢迎大家指出,后面有时间会看一下 java9的 sort实现方式做了哪些优化

转载于:https://my.oschina.net/haitaohu/blog/3007981

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值