Collections.sort in JDK6:MergeSort

       本文是對JDK6中Collections.sort方法的源碼解析,也可以看作是對Comparison method violates its general contract!的後續分析。在JDK6中,該方法底層使用的是經過優化後的歸併排序,廢話不多說,直接看源碼。
public static <T> void sort(List<T> list, Comparator<? super T> c) {
	Object[] a = list.toArray();
	Arrays.sort(a, (Comparator)c);
	ListIterator i = list.listIterator();
	for (int j=0; j<a.length; j++) {
	    i.next();
	    i.set(a[j]);
	}
}
       將list轉成數組,然後調用Arrays.sort方法排序,最後將排好順序的值覆蓋到原list上。
public static <T> void sort(T[] a, Comparator<? super T> c) {
	T[] aux = (T[])a.clone();
	if (c==null)
		mergeSort(aux, a, 0, a.length, 0);
	else
		mergeSort(aux, a, 0, a.length, 0, c);
}
       克隆一個數組,如果比較器爲空,mergeSort(aux, a, 0, a.length, 0);如果比較器不爲空,mergeSort(aux, a, 0, a.length, 0, c);二者內部算法實現一致,只是比較元素的方法不一樣。下面來看歸併排序的實現,看其是如何優化的。
private static void mergeSort(Object[] src,
				  Object[] dest,
				  int low, int high, int off,
				  Comparator c) {
	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 && c.compare(dest[j-1], 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;
	mergeSort(dest, src, low, mid, -off, c);
	mergeSort(dest, src, mid, high, -off, c);

	// 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 (c.compare(src[mid-1], 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 && c.compare(src[p], src[q]) <= 0)
			dest[i] = src[p++];
		else
			dest[i] = src[q++];
	}
}
       我們分段來看。
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 && c.compare(dest[j-1], dest[j])>0; j--)
			swap(dest, j, j-1);
	return;
}
       這裏有一個常量INSERTIONSORT_THRESHOLD。
/**
 * Tuning parameter: list size at or below which insertion sort will be
 * used in preference to mergesort or quicksort.
 */
private static final int INSERTIONSORT_THRESHOLD = 7;
       當數組長度<7時,這裏使用了直接插入排序。直接插入排序的過程可以看這個視頻,插入排序適用於小數列的排序。這裏是JDK6中歸併排序的第一個優化
// Recursively sort halves of dest into src
int destLow  = low;
int destHigh = high;
low  += off;
high += off;
int mid = (low + high) >>> 1;// 中間索引,相當於(low + high) / 2
mergeSort(dest, src, low, mid, -off, c);// 排序左邊
mergeSort(dest, src, mid, high, -off, c);// 排序右邊
       這裏開始遞歸排序,我們不需要關注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 (c.compare(src[mid-1], src[mid]) <= 0) {
   System.arraycopy(src, low, dest, destLow, length);
   return;
}
       左邊和右邊排好序之後,開始合併。這時src[low ~ mid - 1]和src[mid ~ high - 1]都是有序的,這時比較src[mid - 1]和src[mid],如果前者比後者小,那麼皆大歡喜,真個src數組就是有序的了,只需將其複製到目標數組後,就完成了排序,不過這種碰運氣的機率會比較小。這裏是JDK6中歸併排序的第二個優化
// Merge sorted halves (now in src) into dest
for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
	if (q >= high || p < mid && c.compare(src[p], src[q]) <= 0)
		dest[i] = src[p++];
	else
		dest[i] = src[q++];
}
       程序執行到這裏,進行傳統的合併操作。其過程如下圖:
       初始狀態:
       
       循環一次後:
       
       每次都比較src[p]和src[q],將較小的元素存儲到dest[i],不斷的循環比較,直至整個數組都有序。

       最終:
       

      JDK6中的排序是基於傳統的歸併排序做了部分優化,這兩個優化都很簡單,實際上效率並未提高多少。所以在JDK7中將其替換爲TimSort,下回分解。
      (完)
      本文來自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/42060651,轉載請註明。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章