Arrays.sort()的底層實現

1.基本類型(以int爲例)

源碼中的快速排序,主要作了如下幾個方面的優化:
  1)當待排序的數組中的元素個數較少時,源碼中的閥值爲7,採用的是插入排序。儘管插入排序的時間複雜度爲0(n^2),可是當數組元素較少時,插入排序優於快速排序,由於這時快速排序的遞歸操做影響性能。
  2)較好的選擇了劃分元(基準元素)。可以將數組分紅大體兩個相等的部分,避免出現最壞的狀況。例如當數組有序的的狀況下,選擇第一個元素做爲劃分元,將使得算法的時間複雜度達到O(n^2).
  3)根據劃分元 v ,造成不變式 v* (
源碼中選擇劃分元的方法:

 1)當數組大小爲 size=7 時 ,取數組中間元素做爲劃分元。int n=m>>1;(此方法值得借鑑)。
 2)當數組大小size大於7小於等於40時,取首、中、末三個元素中間大小的元素做爲劃分元。
 3)當數組大小 size>40 時 ,從待排數組中較均勻的選擇9個元素,選出一個僞中數作爲劃分元。
 普通的快速排序算法,通過一次劃分後,將劃分元排到素組較中間的位置,左邊的元素小於劃分元,右邊的元素大於劃分元,而沒有將與劃分元相等的元素放在其附近,這一點,在Arrays.sort()中獲得了較大的優化。

舉例:1五、9三、1五、4一、六、1五、2二、七、1五、20
   舉例:1五、9三、1五、4一、六、1五、2二、七、1五、20

  因size大於7小於等於40 ,因此在1五、六、和20 中選擇v = 15 做爲劃分元。
  通過一次換分後: 1五、1五、七、六、4一、20、2二、9三、1五、15. 與劃分元相等的元素都移到了素組的兩邊。
  接下來將與劃分元相等的元素移到數組中間來,造成:七、六、1五、1五、1五、1五、4一、20、2二、93.
  最後遞歸對兩個區間進行排序[七、6]和[4一、20、2二、93].,因此在1五、六、和20 中選擇v = 15 做爲劃分元。
2.Object類型

優化的歸併排序既快速(nlog(n))又穩定。
  對於對象的排序,穩定性很重要。好比成績單,一開始多是按人員的學號順序排好了的,如今讓咱們用成績排,那麼你應該保證,原本張三在李四前面,即便他們成績相同,張三不能跑到李四的後面去。
  而快速排序是不穩定的,並且最壞狀況下的時間複雜度是O(n^2)。
  另外,對象數組中保存的只是對象的引用,這樣屢次移位並不會形成額外的開銷,可是,對象數組對比較次數通常比較敏感,有可能對象的比較比單純數的比較開銷大不少。歸併排序在這方面比快速排序作得更好,這也是選擇它做爲對象排序的一個重要緣由之一。
  排序優化:實現中快排和歸併都採用遞歸方式,而在遞歸的底層,也就是待排序的數組長度小於7時,直接使用冒泡排序,而再也不遞歸下去。
  分析:長度爲6的數組冒泡排序總比較次數最多也就1+2+3+4+5+6=21次,最好狀況下只有6次比較。而快排或歸併涉及到遞歸調用等的開銷,其時間效率在n較小時劣勢就凸顯了,所以這裏採用了冒泡排序,這也是對快速排序極重要的優化。
  
快排部分代碼以下:

package com.util;

public class ArraysPrimitive {
    private ArraysPrimitive() {}

    /**
     * 對指定的 int 型數組按數字升序進行排序。
     */
    public static void sort(int[] a) {
        sort1(a, 0, a.length);
    }

    /**
     * 對指定 int 型數組的指定範圍按數字升序進行排序。
     */
    public static void sort(int[] a, int fromIndex, int toIndex) {
        rangeCheck(a.length, fromIndex, toIndex);
        sort1(a, fromIndex, toIndex - fromIndex);
    }

    private static void sort1(int x[], int off, int len) {
        /*
         * 當待排序的數組中的元素個數小於 7 時,採用插入排序 。
         *
         * 儘管插入排序的時間複雜度爲O(n^2),可是當數組元素較少時, 插入排序優於快速排序,由於這時快速排序的遞歸操做影響性能。
         */
        if (len < 7) {
            for (int i = off; i < len + off; i++)
                for (int j = i; j > off && x[j - 1] > x[j]; j--)
                    swap(x, j, j - 1);
            return;
        }
        /*
         * 當待排序的數組中的元素個數大於 或等於7 時,採用快速排序 。
         *
         * Choose a partition element, v
         * 選取一個劃分元,V
         *
         * 較好的選擇了劃分元(基準元素)。可以將數組分紅大體兩個相等的部分,避免出現最壞的狀況。例如當數組有序的的狀況下,
         * 選擇第一個元素做爲劃分元,將使得算法的時間複雜度達到O(n^2).
         */
        // 當數組大小爲size=7時 ,取數組中間元素做爲劃分元。
        int m = off + (len >> 1);
        // 當數組大小 7<size<=40時,取首、中、末 三個元素中間大小的元素做爲劃分元。
        if (len > 7) {
            int l = off;
            int n = off + len - 1;
            /*
             * 當數組大小  size>40 時 ,從待排數組中較均勻的選擇9個元素,
             * 選出一個僞中數作爲劃分元。
             */
            if (len > 40) {
                int s = len / 8;
                l = med3(x, l, l + s, l + 2 * s);
                m = med3(x, m - s, m, m + s);
                n = med3(x, n - 2 * s, n - s, n);
            }
            // 取出中間大小的元素的位置。
            m = med3(x, l, m, n); // Mid-size, med of 3
        }

        //獲得劃分元V
        int v = x[m];

        // Establish Invariant: v* (<v)* (>v)* v*
        int a = off, b = a, c = off + len - 1, d = c;
        while (true) {
            while (b <= c && x[b] <= v) {
                if (x[b] == v)
                    swap(x, a++, b);
                b++;
            }
            while (c >= b && x[c] >= v) {
                if (x[c] == v)
                    swap(x, c, d--);
                c--;
            }
            if (b > c)
                break;
            swap(x, b++, c--);
        }
        // Swap partition elements back to middle
        int s, n = off + len;
        s = Math.min(a - off, b - a);
        vecswap(x, off, b - s, s);
        s = Math.min(d - c, n - d - 1);
        vecswap(x, b, n - s, s);
        // Recursively sort non-partition-elements
        if ((s = b - a) > 1)
            sort1(x, off, s);
        if ((s = d - c) > 1)
            sort1(x, n - s, s);
    }

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

    /**
     * Swaps x[a .. (a+n-1)] with x[b .. (b+n-1)].
     */
    private static void vecswap(int x[], int a, int b, int n) {
    for (int i=0; i<n; i++, a++, b++)
        swap(x, a, b);
    }

    /**
     * Returns the index of the median of the three indexed integers.
     */
    private static int med3(int x[], int a, int b, int c) {
        return (x[a] < x[b] ? (x[b] < x[c] ? b : x[a] < x[c] ? c : a)
                : (x[b] > x[c] ? b : x[a] > x[c] ? c : a));
    }

    /**
     * Check that fromIndex and toIndex are in range, and throw an
     * appropriate exception if they aren't.
     */
    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);
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133

歸併部分代碼以下:

package com.util;

import java.lang.reflect.Array;

public class ArraysObject {
    private static final int INSERTIONSORT_THRESHOLD = 7;

    private ArraysObject() {}

    public static void sort(Object[] a) {
        //java.lang.Object.clone(),理解深表複製和淺表複製
        Object[] aux = (Object[]) a.clone();
        mergeSort(aux, a, 0, a.length, 0);
    }

    public static void sort(Object[] a, int fromIndex, int toIndex) {
        rangeCheck(a.length, fromIndex, toIndex);
        Object[] aux = copyOfRange(a, fromIndex, toIndex);
        mergeSort(aux, a, fromIndex, toIndex, -fromIndex);
    }

    /**
     * Src is the source array that starts at index 0
     * Dest is the (possibly larger) array destination with a possible offset
     * low is the index in dest to start sorting
     * high is the end index in dest to end sorting
     * off is the offset to generate corresponding low, high in src
     */
    private static void mergeSort(Object[] src, Object[] dest, int low,
            int high, int off) {
        int length = high - low;

        // Insertion sort on smallest arrays
        if (length < INSERTIONSORT_THRESHOLD) {
            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;
        /*
         *  >>>:無符號右移運算符
         *  expression1 >>> expresion2:expression1的各個位向右移expression2
         *  指定的位數。右移後左邊空出的位數用0來填充。移出右邊的位被丟棄。
         *  例如:-14>>>2;  結果爲:1073741820
         */
        int mid = (low + high) >>> 1;
        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) {
            System.arraycopy(src, low, dest, destLow, length);
            return;
        }

        // Merge sorted halves (now in src) into dest
        for (int i = destLow, p = low, q = mid; i < destHigh; i++) {
            if (q >= high || p < mid
                    && ((Comparable) src[p]).compareTo(src[q]) <= 0)
                dest[i] = src[p++];
            else
                dest[i] = src[q++];
        }
    }

    /**
     * Check that fromIndex and toIndex are in range, and throw an appropriate
     * exception if they aren't.
     */
    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);
    }

    public static <T> T[] copyOfRange(T[] original, int from, int to) {
        return copyOfRange(original, from, to, (Class<T[]>) original.getClass());
    }

    public static <T, U> T[] copyOfRange(U[] original, int from, int to,
            Class<? extends T[]> newType) {
        int newLength = to - from;
        if (newLength < 0)
            throw new IllegalArgumentException(from + " > " + to);
        T[] copy = ((Object) newType == (Object) Object[].class)
                ? (T[]) new Object[newLength]
                : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, from, copy, 0,
                Math.min(original.length - from, newLength));
        return copy;
    }

    /**
     * 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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113

輔助理解

package com.lang;

public final class System {
    //System 類不能被實例化。
    private System() {}
    //在 System 類提供的設施中,有標準輸入、標準輸出和錯誤輸出流;對外部定義的屬性
    //和環境變量的訪問;加載文件和庫的方法;還有快速複製數組的一部分的實用方法。
    /**
     * src and dest都必須是同類型或者能夠進行轉換類型的數組.
     * @param      src      the source array.
     * @param      srcPos   starting position in the source array.
     * @param      dest     the destination array.
     * @param      destPos  starting position in the destination data.
     * @param      length   the number of array elements to be copied.
     */
    public static native void arraycopy(Object src, int srcPos, Object dest,
            int destPos, int length);
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

package com.lang.reflect;

public final class Array {
    private Array() {}

    //建立一個具備指定的組件類型和維度的新數組。
    public static Object newInstance(Class<?> componentType, int length)
            throws NegativeArraySizeException {
        return newArray(componentType, length);
    }

    private static native Object newArray(Class componentType, int length)
            throws NegativeArraySizeException;
}

java

相關文章
相關標籤/搜索