Collections在sort()简单分析法源
- 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
版权声明:本文博客原创文章,博客,未经同意,不得转载。