您的位置:首页 > 编程语言

Collections中sort()方法源代码的简单分析

2016-03-04 22:07 337 查看
转载自:http://www.myexception.cn/program/1727176.html

Collections的sort方法代码:
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]);
}
}
这个方法的定义:<T extends Comparable<? super T>> 表示该方法中传递的泛型参数必须实现了Comparable中的compareTo(T o)方法,否则进行不了sort排序。

把这个方法细分为3个步骤:

(1)将list装换成一个对象数组

(2)将这个对象数组传递给Arrays类的sort方法(也就是说collections的sort其实本质是调用了Arrays.sort)

(3)完成排序之后,再一个一个地,把Arrays的元素复制到List中。

Collections方法实际是调用了Arrays的sort方法实现的,再看Arrays的sort方法代码:
public static void sort(Object[] a, int fromIndex, int toIndex) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, fromIndex, toIndex);
else
ComparableTimSort.sort(a, fromIndex, toIndex);
}
注意到1.7下的sort有一个分支判断,当LegacyMergeSort.userRequested为true的情况下,采用legacyMergeSort,否则采用ComparableTimSort。LegacyMergeSort.userRequested的字面意思大概就是“用户请求传统归并排序”的意思,这个分支调用的是与jdk1.5相同的方法来实现功能。

ComparableTimSort是改进后的归并排序,对归并排序在已经反向排好序的输入时表现为O(n^2)的特点做了特别优化。对已经正向排好序的输入减少回溯。对两种情况(一会升序,一会降序)的输入处理比较好(摘自百度百科)。

legacyMergeSort代码:
private static void legacyMergeSort(Object[] a,
int fromIndex, int toIndex) {
rangeCheck(a.length, fromIndex, toIndex);
Object[] aux = copyOfRange(a, fromIndex, toIndex);
mergeSort(aux, a, fromIndex, toIndex, -fromIndex);
}


mergeSort代码:
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;
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++];
}
}


①:这个方法接收Object[] src,Object[] dest两个数组,根据调用它的方法可以看出只需要对dest[]这个数组中的元素进行排序后,传递过来的List<T> list也即排好了序,src[]数组用来进行中介的,也即后面的方法需要调用(所以这两个数组必须区分作用),这里有个判断条件为length < INSERTIONSORT_THRESHOLD,INSERTIONSORT_THRESHOLD为Arrays的一个常量为7,它定义了如果数组元素小于7的话就直接用swap方法排序,提高了程序的执行效率。

②:当数组元素大于7的时候,程序先将数组拆分成低区间和高区间两个区间,再调用两个递归对这两个区间元素进行排序。在递归时还得判断已经划分的区间元素是否还多于7位,如果多于7位的话继续划分成两个区间,这样循环递归调用。在这个方法中,要特别注意src[]和dest[]的参数传递位置,调用递归方法时,是将src[]数组作为排序对象进行排序,src[]排序后,在通过③或④方法将dest[]数组依据src进行排序。最终达到List<T> list排序的结果。

③:如果初始元素个数大于等于7个的话(小于7的直接在①方法排好序返回)进过②方法后,只有两种情况:两个排好序的低区间和高区间。这个方法作用是:如果低区间列表中的最高元素小于高区间列表中的最低元素,则表明该次递归循环的区间段已经排好序,然后将这段数据复制到dest[]数组中。反之则进入方法④。

④:进入该方法表明该次递归循环的低区间的数字最高元素大于高区间列表中的最低元素,也就是说低区间的数组元素值都大于高区间的数组元素值,因此将src中的高区间元素和低区间元素调换放入dest数组中。这样一次递归循环就调用完毕,如果还有循环就继续排序下去,否则排序就已经完成。

ComparableTimSort.sort(a)代码:
static void sort(Object[] a, int lo, int hi) {
rangeCheck(a.length, lo, hi);
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);
binarySort(a, lo, hi, lo + initRunLen);
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.
*/
ComparableTimSort ts = new ComparableTimSort(a);
int minRun = minRunLength(nRemaining);
do {
// Identify next run
int runLen = countRunAndMakeAscending(a, lo, hi);

// 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);
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;
}
 

参考:
http://zoujialiang.iteye.com/blog/866902 http://blog.csdn.net/pickless/article/details/9297895 http://jxlanxin.iteye.com/blog/1814164
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Java源码理解 Java