Arrays.sort源代碼解析

Java Arrays.sort源代碼解析  html

  

  Java Arrays中提供了對全部類型的排序。其中主要分爲Primitive(8種基本類型)和Object兩大類。java

  基本類型:採用調優的快速排序;算法

  對象類型:採用改進的歸併排序。express


1、對於基本類型源碼分析以下(以int[]爲例):數組

  Java對Primitive(int,float等原型數據)數組採用快速排序,對Object對象數組採用歸併排序。對這一區別,sun在<<The Java Tutorial>>中作出的解釋以下:app

  The sort operation uses a slightly optimized merge sort algorithm that is fast and stable:ide

  * Fast: It is guaranteed to run in n log(n) time and runs substantially faster on nearly sorted lists. Empirical tests showed it to be as fast as a highly optimized quicksort. A quicksort is generally considered to be faster than a merge sort but isn't stable and doesn't guarantee n log(n) performance.源碼分析

  * Stable: It doesn't reorder equal elements. This is important if you sort the same list repeatedly on different attributes. If a user of a mail program sorts the inbox by mailing date and then sorts it by sender, the user naturally expects that the now-contiguous list of messages from a given sender will (still) be sorted by mailing date. This is guaranteed only if the second sort was stable.性能

  也就是說,優化的歸併排序既快速(nlog(n))又穩定。測試

  對於對象的排序,穩定性很重要。好比成績單,一開始多是按人員的學號順序排好了的,如今讓咱們用成績排,那麼你應該保證,原本張三在李四前面,即便他們成績相同,張三不能跑到李四的後面去。

  而快速排序是不穩定的,並且最壞狀況下的時間複雜度是O(n^2)。

  另外,對象數組中保存的只是對象的引用,這樣屢次移位並不會形成額外的開銷,可是,對象數組對比較次數通常比較敏感,有可能對象的比較比單純數的比較開銷大不少。歸併排序在這方面比快速排序作得更好,這也是選擇它做爲對象排序的一個重要緣由之一。

  排序優化:實現中快排和歸併都採用遞歸方式,而在遞歸的底層,也就是待排序的數組長度小於7時,直接使用冒泡排序,而再也不遞歸下去。

  分析:長度爲6的數組冒泡排序總比較次數最多也就1+2+3+4+5+6=21次,最好狀況下只有6次比較。而快排或歸併涉及到遞歸調用等的開銷,其時間效率在n較小時劣勢就凸顯了,所以這裏採用了冒泡排序,這也是對快速排序極重要的優化。

 

  源碼中的快速排序,主要作了如下幾個方面的優化:

  1)當待排序的數組中的元素個數較少時,源碼中的閥值爲7,採用的是冒泡排序。儘管冒泡排序的時間複雜度爲0(n^2),可是當數組元素較少時,冒泡排序優於快速排序,由於這時快速排序的遞歸操做影響性能。

  2)較好的選擇了劃分元(基準元素)。可以將數組分紅大體兩個相等的部分,避免出現最壞的狀況。例如當數組有序的的狀況下,選擇第一個元素做爲劃分元,將使得算法的時間複雜度達到O(n^2).

  源碼中選擇劃分元的方法:

    當數組大小爲 size=7 時 ,取數組中間元素做爲劃分元。int n=m>>1;(此方法值得借鑑)

    當數組大小 7<size<=40時,取首、中、末三個元素中間大小的元素做爲劃分元。

    當數組大小 size>40 時 ,從待排數組中較均勻的選擇9個元素,選出一個僞中數作爲劃分元。

  3)根據劃分元 v ,造成不變式 v* (<v)* (>v)* v*

  普通的快速排序算法,通過一次劃分後,將劃分元排到素組較中間的位置,左邊的元素小於劃分元,右邊的元素大於劃分元,而沒有將與劃分元相等的元素放在其附近,這一點,在Arrays.sort()中獲得了較大的優化。

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

  因  7<size<=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 package com.util;  2 
 3 public class ArraysPrimitive {  4     private ArraysPrimitive() {}  5 
 6     /**
 7  * 對指定的 int 型數組按數字升序進行排序。  8      */
 9     public static void sort(int[] a) {  10         sort1(a, 0, a.length);  11  }  12     
 13     /**
 14  * 對指定 int 型數組的指定範圍按數字升序進行排序。  15      */
 16     public static void sort(int[] a, int fromIndex, int toIndex) {  17  rangeCheck(a.length, fromIndex, toIndex);  18         sort1(a, fromIndex, toIndex - fromIndex);  19  }  20 
 21     private static void sort1(int x[], int off, int len) {  22         /*
 23  * 當待排序的數組中的元素個數小於 7 時,採用冒泡排序 。  24  *  25  * 儘管冒泡排序的時間複雜度爲O(n^2),可是當數組元素較少時, 冒泡排序優於快速排序,由於這時快速排序的遞歸操做影響性能。  26          */
 27         if (len < 7) {  28             for (int i = off; i < len + off; i++)  29                 for (int j = i; j > off && x[j - 1] > x[j]; j--)  30                     swap(x, j, j - 1);  31             return;  32  }  33         /*
 34  * 當待排序的數組中的元素個數大於 或等於7 時,採用快速排序 。  35  *  36  * Choose a partition element, v  37  * 選取一個劃分元,V  38  *  39  * 較好的選擇了劃分元(基準元素)。可以將數組分紅大體兩個相等的部分,避免出現最壞的狀況。例如當數組有序的的狀況下,  40  * 選擇第一個元素做爲劃分元,將使得算法的時間複雜度達到O(n^2).  41          */
 42         // 當數組大小爲size=7時 ,取數組中間元素做爲劃分元。
 43         int m = off + (len >> 1);  44         // 當數組大小 7<size<=40時,取首、中、末 三個元素中間大小的元素做爲劃分元。
 45         if (len > 7) {  46             int l = off;  47             int n = off + len - 1;  48             /*
 49  * 當數組大小 size>40 時 ,從待排數組中較均勻的選擇9個元素,  50  * 選出一個僞中數作爲劃分元。  51              */
 52             if (len > 40) {  53                 int s = len / 8;  54                 l = med3(x, l, l + s, l + 2 * s);  55                 m = med3(x, m - s, m, m + s);  56                 n = med3(x, n - 2 * s, n - s, n);  57  }  58             // 取出中間大小的元素的位置。
 59             m = med3(x, l, m, n); // Mid-size, med of 3
 60  }  61         
 62         //獲得劃分元V
 63         int v = x[m];  64         
 65         // Establish Invariant: v* (<v)* (>v)* v*
 66         int a = off, b = a, c = off + len - 1, d = c;  67         while (true) {  68             while (b <= c && x[b] <= v) {  69                 if (x[b] == v)  70                     swap(x, a++, b);  71                 b++;  72  }  73             while (c >= b && x[c] >= v) {  74                 if (x[c] == v)  75                     swap(x, c, d--);  76                 c--;  77  }  78             if (b > c)  79                 break;  80             swap(x, b++, c--);  81  }  82         // Swap partition elements back to middle
 83         int s, n = off + len;  84         s = Math.min(a - off, b - a);  85         vecswap(x, off, b - s, s);  86         s = Math.min(d - c, n - d - 1);  87         vecswap(x, b, n - s, s);  88         // Recursively sort non-partition-elements
 89         if ((s = b - a) > 1)  90  sort1(x, off, s);  91         if ((s = d - c) > 1)  92             sort1(x, n - s, s);  93  }  94     
 95     /**
 96  * Swaps x[a] with x[b].  97      */
 98     private static void swap(int x[], int a, int b) {  99         int t = x[a]; 100         x[a] = x[b]; 101         x[b] = t; 102  } 103     
104     /**
105  * Swaps x[a .. (a+n-1)] with x[b .. (b+n-1)]. 106      */
107     private static void vecswap(int x[], int a, int b, int n) { 108     for (int i=0; i<n; i++, a++, b++) 109  swap(x, a, b); 110  } 111     
112     /**
113  * Returns the index of the median of the three indexed integers. 114      */
115     private static int med3(int x[], int a, int b, int c) { 116         return (x[a] < x[b] ? (x[b] < x[c] ? b : x[a] < x[c] ? c : a) 117                 : (x[b] > x[c] ? b : x[a] > x[c] ? c : a)); 118  } 119 
120     /**
121  * Check that fromIndex and toIndex are in range, and throw an 122  * appropriate exception if they aren't. 123      */
124     private static void rangeCheck(int arrayLen, int fromIndex, int toIndex) { 125         if (fromIndex > toIndex) 126             throw new IllegalArgumentException("fromIndex(" + fromIndex 127                     + ") > toIndex(" + toIndex + ")"); 128         if (fromIndex < 0) 129             throw new ArrayIndexOutOfBoundsException(fromIndex); 130         if (toIndex > arrayLen) 131             throw new ArrayIndexOutOfBoundsException(toIndex); 132  } 133 }
複製代碼

  測試代碼以下:

複製代碼
 1 package com.test;  2 
 3 import com.util.ArraysPrimitive;  4 
 5 public class ArraysTest {  6     public static void main(String[] args) {  7         int [] a={15,93,15,41,6,15,22,7,15,20};  8  ArraysPrimitive.sort(a);  9         for(int i=0;i<a.length;i++){ 10             System.out.print(a[i]+","); 11  } 12         //結果:6,7,15,15,15,15,20,22,41,93,
13  } 14 }
複製代碼

 

2、對於Object類型源碼分析以下:

  部分源代碼(二)以下:

複製代碼
 1 package com.util;  2 
 3 import java.lang.reflect.Array;  4 
 5 public class ArraysObject {  6     private static final int INSERTIONSORT_THRESHOLD = 7;  7 
 8     private ArraysObject() {}  9 
 10     public static void sort(Object[] a) {  11         //java.lang.Object.clone(),理解深表複製和淺表複製
 12         Object[] aux = (Object[]) a.clone();  13         mergeSort(aux, a, 0, a.length, 0);  14  }  15 
 16     public static void sort(Object[] a, int fromIndex, int toIndex) {  17  rangeCheck(a.length, fromIndex, toIndex);  18         Object[] aux = copyOfRange(a, fromIndex, toIndex);  19         mergeSort(aux, a, fromIndex, toIndex, -fromIndex);  20  }  21 
 22     /**
 23  * Src is the source array that starts at index 0  24  * Dest is the (possibly larger) array destination with a possible offset  25  * low is the index in dest to start sorting  26  * high is the end index in dest to end sorting  27  * off is the offset to generate corresponding low, high in src  28      */
 29     private static void mergeSort(Object[] src, Object[] dest, int low,  30             int high, int off) {  31         int length = high - low;  32 
 33         // Insertion sort on smallest arrays
 34         if (length < INSERTIONSORT_THRESHOLD) {  35             for (int i = low; i < high; i++)  36                 for (int j = i; j > low && 
 37                         ((Comparable) dest[j - 1]).compareTo(dest[j]) > 0; j--)  38                     swap(dest, j, j - 1);  39             return;  40  }  41 
 42         // Recursively sort halves of dest into src
 43         int destLow = low;  44         int destHigh = high;  45         low += off;  46         high += off;  47         /*
 48  * >>>:無符號右移運算符  49  * expression1 >>> expresion2:expression1的各個位向右移expression2  50  * 指定的位數。右移後左邊空出的位數用0來填充。移出右邊的位被丟棄。  51  * 例如:-14>>>2; 結果爲:1073741820  52          */
 53         int mid = (low + high) >>> 1;  54         mergeSort(dest, src, low, mid, -off);  55         mergeSort(dest, src, mid, high, -off);  56 
 57         // If list is already sorted, just copy from src to dest. This is an  58         // optimization that results in faster sorts for nearly ordered lists.
 59         if (((Comparable) src[mid - 1]).compareTo(src[mid]) <= 0) {  60  System.arraycopy(src, low, dest, destLow, length);  61             return;  62  }  63 
 64         // Merge sorted halves (now in src) into dest
 65         for (int i = destLow, p = low, q = mid; i < destHigh; i++) {  66             if (q >= high || p < mid  67                     && ((Comparable) src[p]).compareTo(src[q]) <= 0)  68                 dest[i] = src[p++];  69             else
 70                 dest[i] = src[q++];  71  }  72  }  73 
 74     /**
 75  * Check that fromIndex and toIndex are in range, and throw an appropriate  76  * exception if they aren't.  77      */
 78     private static void rangeCheck(int arrayLen, int fromIndex, int toIndex) {  79         if (fromIndex > toIndex)  80             throw new IllegalArgumentException("fromIndex(" + fromIndex  81                     + ") > toIndex(" + toIndex + ")");  82         if (fromIndex < 0)  83             throw new ArrayIndexOutOfBoundsException(fromIndex);  84         if (toIndex > arrayLen)  85             throw new ArrayIndexOutOfBoundsException(toIndex);  86  }  87 
 88     public static <T> T[] copyOfRange(T[] original, int from, int to) {  89         return copyOfRange(original, from, to, (Class<T[]>) original.getClass());  90  }  91 
 92     public static <T, U> T[] copyOfRange(U[] original, int from, int to,  93             Class<? extends T[]> newType) {  94         int newLength = to - from;  95         if (newLength < 0)  96             throw new IllegalArgumentException(from + " > " + to);  97         T[] copy = ((Object) newType == (Object) Object[].class)  98                 ? (T[]) new Object[newLength]  99  : (T[]) Array.newInstance(newType.getComponentType(), newLength); 100         System.arraycopy(original, from, copy, 0, 101                 Math.min(original.length - from, newLength)); 102         return copy; 103  } 104 
105     /**
106  * Swaps x[a] with x[b]. 107      */
108     private static void swap(Object[] x, int a, int b) { 109         Object t = x[a]; 110         x[a] = x[b]; 111         x[b] = t; 112  } 113 }
複製代碼

  測試代碼以下:

 

複製代碼
 1 package com.test;  2 
 3 import com.util.ArraysObject;  4 
 5 public class ArraysObjectSortTest {  6     public static void main(String[] args) {  7         Student stu1=new Student(1001,100.0F);  8         Student stu2=new Student(1002,90.0F);  9         Student stu3=new Student(1003,90.0F); 10         Student stu4=new Student(1004,95.0F); 11         Student[] stus={stu1,stu2,stu3,stu4}; 12         //Arrays.sort(stus);
13  ArraysObject.sort(stus); 14         for(int i=0;i<stus.length;i++){ 15             System.out.println(stus[i].getId()+" : "+stus[i].getScore()); 16  } 17         /* 1002 : 90.0 18  * 1003 : 90.0 19  * 1004 : 95.0 20  * 1001 : 100.0 21          */
22  } 23 } 24 class Student implements Comparable<Student>{ 25     private int id;  //學號
26     private float score;  //成績
27     public Student(){} 28     public Student(int id,float score){ 29         this.id=id; 30         this.score=score; 31  } 32  @Override 33     public int compareTo(Student s) { 34         return (int)(this.score-s.getScore()); 35  } 36     public int getId() { 37         return id; 38  } 39     public void setId(int id) { 40         this.id = id; 41  } 42     public float getScore() { 43         return score; 44  } 45     public void setScore(float score) { 46         this.score = score; 47  } 48 }
複製代碼

  輔助理解代碼:

複製代碼
 1 package com.lang;  2 
 3 public final class System {  4     //System 類不能被實例化。 
 5     private System() {}  6     //在 System 類提供的設施中,有標準輸入、標準輸出和錯誤輸出流;對外部定義的屬性  7     //和環境變量的訪問;加載文件和庫的方法;還有快速複製數組的一部分的實用方法。
 8     /**
 9  * src and dest都必須是同類型或者能夠進行轉換類型的數組. 10  * @param src the source array. 11  * @param srcPos starting position in the source array. 12  * @param dest the destination array. 13  * @param destPos starting position in the destination data. 14  * @param length the number of array elements to be copied. 15      */
16     public static native void arraycopy(Object src, int srcPos, Object dest, 17             int destPos, int length); 18 }
複製代碼
複製代碼
 1 package com.lang.reflect;  2 
 3 public final class Array {  4     private Array() {}  5     
 6     //建立一個具備指定的組件類型和維度的新數組。
 7     public static Object newInstance(Class<?> componentType, int length)  8             throws NegativeArraySizeException {  9         return newArray(componentType, length); 10  } 11 
12     private static native Object newArray(Class componentType, int length) 13             throws NegativeArraySizeException; 14 }
複製代碼

 

 轉自:http://www.cnblogs.com/gw811/archive/2012/10/04/2711746.html

相關文章
相關標籤/搜索