Arrays.sort和Collections.sort实现原理 TimSort

Collections.sort方法底层就是调用的Arrays.sort方法。

写一个例子看源码:

public static void main(String[] args) {
        List strings = Arrays.asList("6", "1", "3", "1","2");
 
        Collections.sort(strings);//sort方法在这里
 
        for (String string : strings) {
 
            System.out.println(string);
        }
}
跟踪代码,发现Collections.sort调用的是list.sort方法:

    @SuppressWarnings("unchecked")
    public static > void sort(List list) {
        list.sort(null);
    }
继续跟踪,发现是用Arrays.sort:

    @SuppressWarnings({"unchecked", "rawtypes"})
    default void sort(Comparator c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }
发现调用的是Arrays.sort(a,c):

    public static void sort(T[] a, Comparator c) {
        if (c == null) {
            sort(a);
        } else {
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, c);
            else
                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        }
    }
没有写比较器c,跟踪LegacyMergeSort,结果如下:

    /**
     * Old merge sort implementation can be selected (for
     * compatibility with broken comparators) using a system property.
     * Cannot be a static boolean in the enclosing class due to
     * circular dependencies. To be removed in a future release.
     */
    static final class LegacyMergeSort {
        private static final boolean userRequested =
            java.security.AccessController.doPrivileged(
                new sun.security.action.GetBooleanAction(
                    "java.util.Arrays.useLegacyMergeSort")).booleanValue();
    }
是一种老的归并排序,我们走的是下面TimSort.sort()这个方法:

    static void sort(T[] a, int lo, int hi, Comparator 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)
            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) {
            int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
            binarySort(a, lo, hi, lo + initRunLen, c);
            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 ts = new TimSort<>(a, c, work, workBase, workLen);
        int minRun = minRunLength(nRemaining);
        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;
    }
    不论是Collections.sort方法或者是Arrays.sort方法,底层实现都是TimSort实现的,这是jdk1.7新增的,以前是归并排序。TimSort算法就是找到已经排好序数据的子序列,然后对剩余部分排序,然后合并起来。

Colletions.sort(list) 与 Arrays.sort(T[])
    Colletions.sort()实际会将list转为数组,然后调用Arrays.sort(),排完了再转回List。
PS. JDK8里,List有自己的sort()方法了,像ArrayList就直接用自己内部的数组来排,而LinkedList, CopyOnWriteArrayList还是要复制出一份数组。

    而Arrays.sort(),对原始类型(int[],double[],char[],byte[]),JDK6里用的是快速排序,对于对象类型(Object[]),JDK6则使用归并排序。为什么要用不同的算法呢?

JDK7的进步
    到了JDK7,快速排序升级为双基准快排(双基准快排vs三路快排);归并排序升级为归并排序的改进版TimSort,一个JDK的自我进化。(TimSort例子http://xieyan87.com/2015/08/timsort%E7%AE%97%E6%B3%95%E7%AE%80%E4%BB%8B/)

JDK8的进步
    再到了JDK8, 对大集合增加了Arrays.parallelSort()函数,使用fork-Join框架,充分利用多核,对大的集合进行切分然后再归并排序,而在小的连续片段里,依然使用TimSort与DualPivotQuickSort。

 

TimSort:
    Timsort是一种结合了归并排序和插入排序的混合算法,由Tim Peters在2002年提出,并且已经成为Python 2.3版本以后内置排序算法,并且Java SE 7, Android平台,GNU Octave也引入了这一排序算法。简单来说,这个算法可以概括为两步:
1) 第一步就是把待排数组划分成一个个run,当然run不能太短,如果长度小于minrun这个阈值,则用插入排序进行扩充;
2) 第二步将run入栈,当栈顶的run的长度满足:runLen[n-2] <= runLen[n-1] + runLen[n]或者 runLen[n-1] <= runLen[n], 则对两个短run归并为一个新run,则到只剩栈顶元素时排序也完成了。

你可能感兴趣的:(算法)