Java TimSortアルゴリズムのソースノートを読む

32141 ワード

Javaコンテナのソースコードを見るつもりでした.でも最初はArraysという類に慣れていなかったので、ついでにArraysという類を見せてもらいました.Arraysクラスにはアーキテクチャや難点はありませんが、Arraysに関連する2つのソートアルゴリズムは興味深いようです.ついでにTimSortアルゴリズムとダブルポインタのクイックソートも検討してみましょう.
まず強調しますが、これは安定したソートアルゴリズムです.
コードを見た後、このアルゴリズムは思ったほど難しくないと思います.論理が明確で,アルゴリズム全体の最大の特徴は配列にすでに存在する順序を十分に利用することである.集計の過程でGalloping Mode(翻訳すると飛ぶモードと呼ぶことができる)があり、これはソートアルゴリズム全体で最も尋常ではない場所である.簡単な理解では、集計中に2つの数列があり、比較すると、{MIN_GALLOP}の要素が連続して別の数列の最初の要素よりも小さいので、後ろにどれだけの要素が別の数列の最初の要素より小さいかを数えるべきです.数え終わったら一度copyを過ぎて、copyの回数を減らします.MIN_GALLOPは、統計的に最適化された結果であるべき動的に調整可能な値である.
アルゴリズム自体の魅力を除いて、著者のコードは簡潔に書かれています.読んで楽しめました.皆さんは興味があれば自分で読んでください.私は下にコードを見ている間のコメントを貼っています.論理に対するすべての解釈は注釈に含まれている.読む方法はstatic void sort(T[] a, Comparator super T> c)static void sort(T[] a, int lo, int hi, Comparator super T> c)の2つの方法から始まり、論理に沿って下に読めばいいです.
前述したGalloping Modeに加えて、ソースコードにはrunという概念があり、順序付けされた数列と理解することができます.
JAvaのソースコードはjavaインストールパスの下のsrc.zipファイル内にあり、ネット上でダウンロードする必要はありません.例えば、私のubuntuシステムは/usr/lib/jvm/java-7-oracle/src.zip内にあります.
import java.util.Arrays;
import java.util.Comparator;

/**
 * Created by yxf on 16-5-30.
 *    TimSort   java        ,               ,        。
 *
 */
class TimSort {
    /**
     *            。                    。           ,          。
     * 
     *         2  。Tim Perter  C           64,             32   。       ,    2    ,       {@link # minRunLength}    。
     *         ,           stackLen  ,            。
     */
    private static final int MIN_MERGE = 32;

    /**
     *         
     */
    private final T[] a;

    /**
     *         
     */
    private final Comparator super T> c;

    /**
     *             
     *        ,       
     */
    private static final int MIN_GALLOP = 7;

    private int minGallop = MIN_GALLOP;

    /**
     *               ,              。
     *  C          ,         ,            。             
     */
    private static final int INITIAL_TMP_STORAGE_LENGTH = 256;


    /**
     *     ,         ,       Object[],   T[]
     */
    private T[] tmp;

    /**
     *       run   。  run i    runBase[i]  ,     runLen[i]。
     *          run        run   。
     *            :
     * runBase[i] + runLen[i] == runBase[i+1];
     **/

    private int stackSize = 0; //  run   
    private final int[] runBase;
    private final int[] runLen;

    /**
     *                     。
     *                        。
     */
    private TimSort(T[] a, Comparator super T> c) {
        this.a = a;
        this.c = c;

        //             。SuppressWainings              
        //             ,          java   。
        //                INITIAL_TMP_STORAGE_LENGTH
        int len = a.length;
        @SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
        T[] newArray = (T[]) new Object[len < 2 * INITIAL_TMP_STORAGE_LENGTH ?
                len >>> 1 : INITIAL_TMP_STORAGE_LENGTH];
        tmp = newArray;

        /**
         *        run     ,         。
         * C              85,                  。  ,
         *                  。
         *  MIN_MERGE     ,  ‘   ’           。
         * */
        int stackLen = (len < 120 ? 5 :
                                 len < 1542 ? 10 :
                                 len < 119151 ? 24 : 40);
        runBase = new int[stackLen];
        runLen = new int[stackLen];
    }

    static  void sort(T[] a, Comparator super T> c) {
        sort(a, 0, a.length, c);
    }

    static  void sort(T[] a, int lo, int hi, Comparator super T> c) {
        if (c == null) {
            Arrays.sort(a, lo, hi);
            return;
        }

        rangeCheck(a.length, lo, hi);
        int nRemaining = hi - lo;
        if (nRemaining < 2)
            return;  //    0  1        。

        //   MIN_MERGE             ,       
        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.
         *
         *             ,         run   ,            。
         *
         *     ,      。          (natural run),                 
         *    minRun       。             ,      。
         */
        TimSort ts = new TimSort<>(a, c); //  TimSort  ,      
        int minRun = minRunLength(nRemaining);
        do {
            //           ,        
            int runLen = countRunAndMakeAscending(a, lo, hi, c);

            // If run is short, extend to min(minRun, nRemaining)
            //             minRun,   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();

            //     runLen  ,            
            lo += runLen;
            //              runLen
            nRemaining -= runLen;
        } while (nRemaining != 0);

        // Merge all remaining runs to complete sort
        assert lo == hi;
        ts.mergeForceCollapse();
        assert ts.stackSize == 1;
    }

    /**
     *           
     *
     *                     。             。     
     *     O(n log n)      O(n^2)     。
     *
     *                        。            lo(    ) 
     * start(     )           。
     *
     * @param a           
     * @param lo                  
     * @param hi                      
     * @param start                   ,   (lo <= start <= hi)
     * @param c             
     */
    @SuppressWarnings("fallthrough")
    private static  void binarySort(T[] a, int lo, int hi, int start,
                                       Comparator super T> c) {
        assert lo <= start && start <= hi;
        //  start      ,     ;          。
        if (start == lo)
            start++;
        // start    ,          
        for (; start < hi; start++) {
            //pivot           ,
            T pivot = a[start];

            // Set left (and right) to the index where a[start] (pivot) belongs
            //  pivot             left right
            int left = lo;
            int right = start;
            assert left <= right;

            /*
             *      :
             *   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;
            }
            assert left == right;

            /**
             *   ,     :
             * pivot >= [lo, left) && pivot < [left,start)
             *   ,pivot     left     ,     [left,start)            
             *     。  pivot          ,left             ,
             *            。
             */
            int n = start - left;  //          

            // switch        ,1-2          System.arraycopy 。
            // (         ,switch       )
            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);
            }
            //     , pivot           ,  left    
            a[left] = pivot;
        }
    }

    /**
     *       TimSort         ,                。
     *      ,        ;     ,     ,       ,
     *                         。
     *              ,lo          。
     *
     * A run is the longest ascending sequence with:
     *
     * a[lo] <= a[lo + 1] <= a[lo + 2] <= ...
     *
     * or the longest descending sequence with:
     *
     * a[lo] >  a[lo + 1] >  a[lo + 2] >  ...
     *
     *           ,          ,                      ,
     *              。
     *
     * @param a         
     * @param lo run        
     * @param hi run              ,    lo int countRunAndMakeAscending(T[] a, int lo, int hi,
                                                    Comparator super T> c) {
        assert lo < hi;
        int runHi = lo + 1;
        if (runHi == hi)
            return 1;

        //            ,    ,   
        if (c.compare(a[runHi++], a[lo]) < 0) { //         ,       
            while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)
                runHi++;
            reverseRange(a, lo, runHi);
        } else {                              //         ,      
            while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)
                runHi++;
        }

        return runHi - lo;
    }

    /**
     *             
     *
     * @param a      
     * @param lo          
     * @param hi                
     */
    private static void reverseRange(Object[] a, int lo, int hi) {
        hi--;
        while (lo < hi) {
            Object t = a[lo];
            a[lo++] = a[hi];
            a[hi--] = t;
        }
    }

    /**
     *            ,         ,     ,              
     *    。{@link #binarySort}.
     *
     *     ,        :
     *
     *    n < MIN_MERGE,      n。(   ,         );
     *    n    2  ,   n / 2;
     *             k,   MIN_MERGE/2 <= k <= MIN_MERGE,
     *          n/k          2  。
     *                   。
     *
     * @param n           
     * @return          
     *          
     */
    private static int minRunLength(int n) {
        assert n >= 0;
        int r = 0;      //      2      1
        while (n >= MIN_MERGE) {
            r |= (n & 1);
            n >>= 1;
        }
        return n + r;
    }

    /**
     * Pushes the specified run onto the pending-run stack.
     *                  
     *
     * @param runBase             
     * @param runLen         
     */
    private void pushRun(int runBase, int runLen) {
        this.runBase[stackSize] = runBase;
        this.runLen[stackSize] = runLen;
        stackSize++;
    }

    /**
     *             ,                      ,
     *            
     *
     * 1. runLen[i - 3] > runLen[i - 2] + runLen[i - 1]
     * 2. runLen[i - 2] > runLen[i - 1]
     *
     *                        。            
     *             。
     *
     *      ,    2048。
     */
    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
            }
        }
    }

    /**
     *             ,        。               
     */
    private void mergeForceCollapse() {
        while (stackSize > 1) {
            int n = stackSize - 2;
            if (n > 0 && runLen[n - 1] < runLen[n + 1])
                n--;
            mergeAt(n);
        }
    }

    /**
     *       ,      key,              ;       
     *  key    (      ),             。
     *
     *   :        ,      ,                  ,    
     *            。     ,     ,       log(n),      
     *         。
     *
     * @param key       key
     * @param a           
     * @param base              
     * @param len         ,   len > 0
     * @param hint        , 0 <= hint <= len;         
     * @param c      ,        
     * @return        k,   0 <= k <=n,     a[b + k - 1] < a[b + k]
     *    key      a[base + k],
     *   a[base,base+k) < key && key <=a [base + k, base + len)
     */
    private static  int gallopLeft(T key, T[] a, int base, int len, int hint,
                                      Comparator super T> c) {
        assert len > 0 && hint >= 0 && hint < len;
        int lastOfs = 0;
        int ofs = 1;
        if (c.compare(key, a[base + hint]) > 0) { // key > a[base+hint]
            //     ,   a[base+hint+lastOfs] < key <= a[base+hint+ofs]
            int maxOfs = len - hint;
            while (ofs < maxOfs && c.compare(key, a[base + hint + ofs]) > 0) {
                lastOfs = ofs;
                ofs = (ofs << 1) + 1;
                if (ofs <= 0)   // int overflow
                    ofs = maxOfs;
            }
            if (ofs > maxOfs)
                ofs = maxOfs;

            //    ofs      ,     a[base+hint+lastOfs] < key <= a[base+hint+ofs]
            //    
            // ofs:     1   3   7  15  31  63 2^n-1 ... maxOfs
            // lastOfs: 0   1   3   7  15  31 2^(n-1)-1  < ofs


            //      offset   hint ,       
            lastOfs += hint;
            ofs += hint;
        } else { // key <= a[base + hint]
            //     ,  [base+hint-ofs] < key <= a[base+hint-lastOfs]
            final int maxOfs = hint + 1;
            while (ofs < maxOfs && c.compare(key, a[base + hint - ofs]) <= 0) {
                lastOfs = ofs;
                ofs = (ofs << 1) + 1;
                if (ofs <= 0)   // int overflow
                    ofs = maxOfs;
            }
            if (ofs > maxOfs)
                ofs = maxOfs;
            //   ofs        
            // ofs:     1   3   7  15  31  63 2^n-1 ... maxOfs
            // lastOfs: 0   1   3   7  15  31 2^(n-1)-1  < ofs

            // Make offsets relative to base
            int tmp = lastOfs;
            lastOfs = hint - ofs;
            ofs = hint - tmp;
        }
        assert -1 <= lastOfs && lastOfs < ofs && ofs <= len;

        /*
         *        a[base+lastOfs] < key <= a[base+ofs],   ,key   lastOfs 
         *   ,    ofs。 base+lastOfs-1  base+ofs          。
         */
        lastOfs++;
        while (lastOfs < ofs) {
            int m = lastOfs + ((ofs - lastOfs) >>> 1);

            if (c.compare(key, a[base + m]) > 0)
                lastOfs = m + 1;  // a[base + m] < key
            else
                ofs = m;          // key <= a[base + m]
        }
        assert lastOfs == ofs;    // so a[base + ofs - 1] < key <= a[base + ofs]
        return ofs;
    }

    /**
     *  gallopLeft  ,        key         ,                
     *      
     *
     * @param key               
     * @param a          
     * @param base                
     * @param len           
     * @param hint        ,0 <= hint < len.          ,    。
     * @param c            
     * @return      k,    0 <= k <= n    a[b + k - 1] <= key < a[b + k]
     */
    private static  int gallopRight(T key, T[] a, int base, int len,
                                       int hint, Comparator super T> c) {
        assert len > 0 && hint >= 0 && hint < len;

        int ofs = 1;
        int lastOfs = 0;
        if (c.compare(key, a[base + hint]) < 0) {
            // Gallop left until a[b+hint - ofs] <= key < a[b+hint - lastOfs]
            int maxOfs = hint + 1;
            while (ofs < maxOfs && c.compare(key, a[base + hint - ofs]) < 0) {
                lastOfs = ofs;
                ofs = (ofs << 1) + 1;
                if (ofs <= 0)   // int overflow
                    ofs = maxOfs;
            }
            if (ofs > maxOfs)
                ofs = maxOfs;

            // Make offsets relative to b
            int tmp = lastOfs;
            lastOfs = hint - ofs;
            ofs = hint - tmp;
        } else { // a[b + hint] <= key
            // Gallop right until a[b+hint + lastOfs] <= key < a[b+hint + ofs]
            int maxOfs = len - hint;
            while (ofs < maxOfs && c.compare(key, a[base + hint + ofs]) >= 0) {
                lastOfs = ofs;
                ofs = (ofs << 1) + 1;
                if (ofs <= 0)   // int overflow
                    ofs = maxOfs;
            }
            if (ofs > maxOfs)
                ofs = maxOfs;

            // Make offsets relative to b
            lastOfs += hint;
            ofs += hint;
        }
        assert -1 <= lastOfs && lastOfs < ofs && ofs <= len;

        /*
         * Now a[b + lastOfs] <= key < a[b + ofs], so key belongs somewhere to
         * the right of lastOfs but no farther right than ofs.  Do a binary
         * search, with invariant a[b + lastOfs - 1] <= key < a[b + ofs].
         */
        lastOfs++;
        while (lastOfs < ofs) {
            int m = lastOfs + ((ofs - lastOfs) >>> 1);

            if (c.compare(key, a[base + m]) < 0)
                ofs = m;          // key < a[b + m]
            else
                lastOfs = m + 1;  // a[b + m] <= key
        }
        assert lastOfs == ofs;    // so a[b + ofs - 1] <= key < a[b + ofs]
        return ofs;
    }

    /**
     *        i i+1          。 i       ,        。
     *     i == stackSize - 2 || i == stackSize - 3
     *
     * @param i               
     */
    private void mergeAt(int i) {
        //  
        assert stackSize >= 2;
        assert i >= 0;
        assert i == stackSize - 2 || i == stackSize - 3;
        //     
        int base1 = runBase[i];
        int len1 = runLen[i];
        int base2 = runBase[i + 1];
        int len2 = runLen[i + 1];
        assert len1 > 0 && len2 > 0;
        assert base1 + len1 == base2;

        /*
         *            ;  i == stackSize - 3            
         *      ,           。i+1         i    ,  
         * i+1        
         */
        runLen[i] = len1 + len2;
        if (i == stackSize - 3) {
            runBase[i + 1] = runBase[i + 2];
            runLen[i + 1] = runLen[i + 2];
        }
        //i+1   ,         
        stackSize--;

        /*
         *                            ,                。
         *        ,     。
         */
        int k = gallopRight(a[base2], a, base1, len1, 0, c);
        assert k >= 0;
        //            ,            
        base1 += k;
        len1 -= k;
        //     2            1   ,       ,
        // !!!     2             1  ,              !!!
        if (len1 == 0)
            return;

        /*
         *      ,   1       (a[base1+len1-1])       2     (            ,        ),
         *                    。  len2       ,         。
         */
        len2 = gallopLeft(a[base1 + len1 - 1], a, base2, len2, len2 - 1, c);
        assert len2 >= 0;
        if (len2 == 0)
            return;

        //            ,          ,       min(len1,len2)   
        //       
        if (len1 <= len2)
            mergeLo(base1, len1, base2, len2);
        else
            mergeHi(base1, len1, base2, len2);
    }

    /**
     *                  ,        。
     *                                ;          
     *             
     *
     *     ,     len1 <= len2     ;      mergeHi   len1 >= len2
     *      。len1==len2            
     *
     * @param base1 index of first element in first run to be merged
     * @param len1  length of first run to be merged (must be > 0)
     * @param base2 index of first element in second run to be merged
     *              (must be aBase + aLen)
     * @param len2  length of second run to be merged (must be > 0)
     */
    private void mergeLo(int base1, int len1, int base2, int len2) {
        assert len1 > 0 && len2 > 0 && base1 + len1 == base2;

        //             
        T[] a = this.a; // For performance
        T[] tmp = ensureCapacity(len1);
        System.arraycopy(a, base1, tmp, 0, len1);

        int cursor1 = 0;       //       
        int cursor2 = base2;   //   2   ,          
        int dest = base1;      //        

        //               ,           ,              
        a[dest++] = a[cursor2++];

        //   2         ,           ,       
        //       1     copy   
        if (--len2 == 0) {
            System.arraycopy(tmp, cursor1, a, dest, len1);
            return;
        }
        //   1         ,           ,     ,    2    
        //     。       1           2        ,          
        if (len1 == 1) {
            System.arraycopy(a, cursor2, a, dest, len2);
            a[dest + len2] = tmp[cursor1]; // Last elt of run 1 to end of merge
            return;
        }

        Comparator super T> c = this.c;  //         

        int minGallop = this.minGallop;    //  "    "       "     "      "

        //    break        Java    
        outer:
        while (true) {
            /*
            *                          ,     ,      
            *   
            * */
            int count1 = 0; //   1       2    
            int count2 = 0; //   2       1    

            /*
            *                 ,     count1 count2,
            *             ,      
            * */
            do {
                assert len1 > 1 && len2 > 0;
                if (c.compare(a[cursor2], tmp[cursor1]) < 0) {
                    a[dest++] = a[cursor2++];
                    count2++;
                    count1 = 0;

                    //   2            
                    if (--len2 == 0)
                        break outer;
                } else {
                    a[dest++] = tmp[cursor1++];
                    count1++;
                    count2 = 0;
                    //     1                 
                    if (--len1 == 1)
                        break outer;
                }

            /*
            *         count1 < minGallop && count2  1 && len2 > 0;
                // gallopRight           
                count1 = gallopRight(a[cursor2], tmp, cursor1, len1, 0, c);
                if (count1 != 0) {
                    System.arraycopy(tmp, cursor1, a, dest, count1);
                    dest += count1;
                    cursor1 += count1;
                    len1 -= count1;
                    if (len1 <= 1) //          
                        break outer;
                }
                a[dest++] = a[cursor2++];
                if (--len2 == 0) //         
                    break outer;

                count2 = gallopLeft(tmp[cursor1], a, cursor2, len2, 0, c);
                if (count2 != 0) {
                    System.arraycopy(a, cursor2, a, dest, count2);
                    dest += count2;
                    cursor2 += count2;
                    len2 -= count2;
                    if (len2 == 0)
                        break outer;
                }
                a[dest++] = tmp[cursor1++];
                if (--len1 == 1)
                    break outer;
                //                  ,           ,
                //              ,             ?
                minGallop--;
            } while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP); //           ,      s


            if (minGallop < 0)
                minGallop = 0;

            //  ,           ,            ,      ,      
            minGallop += 2;
        }  //outer   


        this.minGallop = minGallop < 1 ? 1 : minGallop;  // Write back to field

        //        
        if (len1 == 1) {
            assert len2 > 0;
            System.arraycopy(a, cursor2, a, dest, len2);
            a[dest + len2] = tmp[cursor1]; //  Last elt of run 1 to end of merge
        } else if (len1 == 0) {
            //    1       ,   2       ,  ,     1  ,  2    
            throw new IllegalArgumentException(
                    "Comparison method violates its general contract!");
        } else {
            assert len2 == 0;
            assert len1 > 1;
            System.arraycopy(tmp, cursor1, a, dest, len1);
        }
    }

    /**
     * Like mergeLo, except that this method should be called only if
     * len1 >= len2; mergeLo should be called if len1 <= len2.  (Either method
     * may be called if len1 == len2.)
     *
     * @param base1 index of first element in first run to be merged
     * @param len1  length of first run to be merged (must be > 0)
     * @param base2 index of first element in second run to be merged
     *              (must be aBase + aLen)
     * @param len2  length of second run to be merged (must be > 0)
     */
    private void mergeHi(int base1, int len1, int base2, int len2) {
        assert len1 > 0 && len2 > 0 && base1 + len1 == base2;

        // Copy second run into temp array
        T[] a = this.a; // For performance
        T[] tmp = ensureCapacity(len2);
        System.arraycopy(a, base2, tmp, 0, len2);

        int cursor1 = base1 + len1 - 1;  // Indexes into a
        int cursor2 = len2 - 1;          // Indexes into tmp array
        int dest = base2 + len2 - 1;     // Indexes into a

        // Move last element of first run and deal with degenerate cases
        a[dest--] = a[cursor1--];
        if (--len1 == 0) {
            System.arraycopy(tmp, 0, a, dest - (len2 - 1), len2);
            return;
        }
        if (len2 == 1) {
            dest -= len1;
            cursor1 -= len1;
            System.arraycopy(a, cursor1 + 1, a, dest + 1, len1);
            a[dest] = tmp[cursor2];
            return;
        }

        Comparator super T> c = this.c;  // Use local variable for performance
        int minGallop = this.minGallop;    //  "    "       "     "      "
        outer:
        while (true) {
            int count1 = 0; // Number of times in a row that first run won
            int count2 = 0; // Number of times in a row that second run won

            /*
             * Do the straightforward thing until (if ever) one run
             * appears to win consistently.
             */
            do {
                assert len1 > 0 && len2 > 1;
                if (c.compare(tmp[cursor2], a[cursor1]) < 0) {
                    a[dest--] = a[cursor1--];
                    count1++;
                    count2 = 0;
                    if (--len1 == 0)
                        break outer;
                } else {
                    a[dest--] = tmp[cursor2--];
                    count2++;
                    count1 = 0;
                    if (--len2 == 1)
                        break outer;
                }
            } while ((count1 | count2) < minGallop);

            /*
             * One run is winning so consistently that galloping may be a
             * huge win. So try that, and continue galloping until (if ever)
             * neither run appears to be winning consistently anymore.
             */
            do {
                assert len1 > 0 && len2 > 1;
                count1 = len1 - gallopRight(tmp[cursor2], a, base1, len1, len1 - 1, c);
                if (count1 != 0) {
                    dest -= count1;
                    cursor1 -= count1;
                    len1 -= count1;
                    System.arraycopy(a, cursor1 + 1, a, dest + 1, count1);
                    if (len1 == 0)
                        break outer;
                }
                a[dest--] = tmp[cursor2--];
                if (--len2 == 1)
                    break outer;

                count2 = len2 - gallopLeft(a[cursor1], tmp, 0, len2, len2 - 1, c);
                if (count2 != 0) {
                    dest -= count2;
                    cursor2 -= count2;
                    len2 -= count2;
                    System.arraycopy(tmp, cursor2 + 1, a, dest + 1, count2);
                    if (len2 <= 1)  // len2 == 1 || len2 == 0
                        break outer;
                }
                a[dest--] = a[cursor1--];
                if (--len1 == 0)
                    break outer;
                minGallop--;
            } while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);
            if (minGallop < 0)
                minGallop = 0;
            minGallop += 2;  // Penalize for leaving gallop mode
        }  // End of "outer" loop
        this.minGallop = minGallop < 1 ? 1 : minGallop;  // Write back to field

        if (len2 == 1) {
            assert len1 > 0;
            dest -= len1;
            cursor1 -= len1;
            System.arraycopy(a, cursor1 + 1, a, dest + 1, len1);
            a[dest] = tmp[cursor2];  // Move first elt of run2 to front of merge
        } else if (len2 == 0) {
            throw new IllegalArgumentException(
                    "Comparison method violates its general contract!");
        } else {
            assert len1 == 0;
            assert len2 > 0;
            System.arraycopy(tmp, 0, a, dest - (len2 - 1), len2);
        }
    }

    /**
     *                     ,                。
     *           ,         。
     *
     *         ,          ,    ;          ,   
     *     。     ,                  * 2,
     *              1/2。      2        。
     *
     * @param minCapacity            
     * @return tmp     
     */
    private T[] ensureCapacity(int minCapacity) {
        //           ,             ;
        //      ,          
        if (tmp.length < minCapacity) {
            //           minCapacity 2  。     ,      。
            //
            //          k,       :
            // 00000000 10000000 00000000 00000000  k
            // 00000000 11000000 00000000 00000000  k |= k >> 1;
            // 00000000 11110000 00000000 00000000  k |= k >> 2;
            // 00000000 11111111 00000000 00000000  k |= k >> 4;
            // 00000000 11111111 11111111 00000000  k |= k >> 8;
            // 00000000 11111111 11111111 11111111  k |= k >> 16
            //                 ,            bit     1
            //    k++            minCapacity   2  
            //     6
            int newSize = minCapacity;
            newSize |= newSize >> 1;
            newSize |= newSize >> 2;
            newSize |= newSize >> 4;
            newSize |= newSize >> 8;
            newSize |= newSize >> 16;
            newSize++;

            if (newSize < 0) // Not bloody likely!          bug 
                newSize = minCapacity;
            else
                newSize = Math.min(newSize, a.length >>> 1);

            @SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
            T[] newArray = (T[]) new Object[newSize];
            tmp = newArray;
        }
        return tmp;
    }

    /**
     *     fromIndex toIndex      ,       
     *
     * @param arrayLen         
     * @param fromIndex       
     * @param toIndex         
     * @throws IllegalArgumentException       if fromIndex > toIndex
     * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 or toIndex > arrayLen
     */
    private static void rangeCheck(int arrayLen, int fromIndex, int toIndex) {
        if (fromIndex > toIndex)
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                    ") > toIndex(" + toIndex + ")");
        if (fromIndex < 0)
            throw new ArrayIndexOutOfBoundsException(fromIndex);
        if (toIndex > arrayLen)
            throw new ArrayIndexOutOfBoundsException(toIndex);
    }
}