jdk1.7 和 jdk1.8做对比阅读
jdk1.7
public static <T extends Comparable<? super T>> void sort(List<T> var0) { Object[] var1 = var0.toArray(); //对var1排序后 Arrays.sort(var1); ListIterator var2 = var0.listIterator(); //重新覆盖到 var0 for(int var3 = 0; var3 < var1.length; ++var3) { var2.next(); var2.set((Comparable)var1[var3]); } } public static <T> void sort(List<T> var0, Comparator<? super T> var1) { Object[] var2 = var0.toArray(); Arrays.sort(var2, var1); ListIterator var3 = var0.listIterator(); for(int var4 = 0; var4 < var2.length; ++var4) { var3.next(); var3.set(var2[var4]); } }
jdk1.8
public static <T extends Comparable<? super T>> void sort(List<T> list) { list.sort(null); }
default void sort(Comparator<? super E> c) { Object[] a = this.toArray(); Arrays.sort(a, (Comparator) c); ListIterator<E> i = this.listIterator(); for (Object e : a) { i.next(); i.set((E) e); } }
最终都是走到了 Arrays.sort方法
public static <T> void sort(T[] a, Comparator<? super T> c) { if (c == null) { sort(a); } else { if (LegacyMergeSort.userRequested) legacyMergeSort(a, c); else TimSort.sort(a, 0, a.length, c, null, 0, 0); } }
这里注意 LegacyMergeSort.userRequested ,读取的java.util.Arrays.useLegacyMergeSort 用来兼容历史排序方法,比如1.7版本的jdk想使用1.6的排序方法,就需要配置这个参数 java.util.Arrays.useLegacyMergeSort=true
static final class LegacyMergeSort { private static final boolean userRequested = java.security.AccessController.doPrivileged( new sun.security.action.GetBooleanAction( "java.util.Arrays.useLegacyMergeSort")).booleanValue(); }
接下来先看一下1.7以前使用的哪种排序办法
private static void mergeSort(Object[] src, Object[] dest, int low, int high, int off) { int length = high - low; // 这里是一个优化点,如果是一个比较小的数组 直接使用插入排序 if (length < INSERTIONSORT_THRESHOLD) {//这里默认是 7数组长度小于7 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;//二进制右移一位相当于除以2 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) {//如果两部分分别排序后,左侧的最大 小于右侧最小 说明整体 src已经有序 直接复制到 dest System.arraycopy(src, low, dest, destLow, length); return; } // for(int i = destLow, p = low, q = mid; i < destHigh; i++) {//分别遍历左侧 和右侧 合并到dest,需要考虑到某一个先遍历到头后的情景 q>=high 和 p<mid if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0) dest[i] = src[p++]; else dest[i] = src[q++]; } } /** * 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.7 和1.8的实现,他们的变化不变,这里就只贴1.8了
根据是否有 Comparator 参数分别为,带Comparator参数
static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c, T[] work, int workBase, int workLen) { assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length; int nRemaining = hi - lo; if (nRemaining < 2)//长度小于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) {//长度小于32 直接返回 int initRunLen = countRunAndMakeAscending(a, lo, hi, c);//获取有序段,即lo 到几是有序的(如果是从大到小有序的话,会对这一小段进行反转) //执行到这里,0,到initRunLen 是有序的了 binarySort(a, lo, hi, lo + initRunLen, c);//二分插入排序,就是把initRunLen位置后面的数据,通过二分搜索定位的方式插入到前面有序段落里面 // 这里想吐槽一下 和 1.7之前的直接对小数组直接进行插入排序 感觉复杂了好多,不过确实复杂度变小了 从O(n^2) 变为了 O(nlogn) 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. */ TimSort<T> ts = new TimSort<>(a, c, work, workBase, workLen); int minRun = minRunLength(nRemaining);//获取有序run块的最小长度 do { // Identify next run int runLen = countRunAndMakeAscending(a, lo, hi, c);//获取有序递增块的长度 // 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, c); 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; }
//用于获取一个递增连续区域长度 private static int countRunAndMakeAscending(Object[] a, int lo, int hi) { assert lo < hi; int runHi = lo + 1; if (runHi == hi) return 1; // Find end of run, and reverse range if descending if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) { // Descending while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0) runHi++; reverseRange(a, lo, runHi); } else { // Ascending while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) >= 0) runHi++; } return runHi - lo; }
//二分插入排序 private static void binarySort(Object[] a, int lo, int hi, int start) { assert lo <= start && start <= hi; if (start == lo) start++; for ( ; start < hi; start++) { Comparable pivot = (Comparable) a[start]; // Set left (and right) to the index where a[start] (pivot) belongs int left = lo; int right = start; assert left <= right; /* * Invariants: * pivot >= all in [lo, left). * pivot < all in [right, start). */ while (left < right) { int mid = (left + right) >>> 1; if (pivot.compareTo(a[mid]) < 0) right = mid; else left = mid + 1; } assert left == right; /* * The invariants still hold: pivot >= all in [lo, left) and * pivot < all in [left, start), so pivot belongs at left. Note * that if there are elements equal to pivot, left points to the * first slot after them -- that's why this sort is stable. * Slide elements over to make room for pivot. */ int n = start - left; // The number of elements to move // Switch is just an optimization for arraycopy in default case switch (n) { case 2: a[left + 2] = a[left + 1]; case 1: a[left + 1] = a[left]; break; default: System.arraycopy(a, left, a, left + 1, n); } a[left] = pivot; } }
接下来着重看一下最后的排序步骤
private void pushRun(int runBase, int runLen) { this.runBase[stackSize] = runBase; this.runLen[stackSize] = runLen; stackSize++; }
private void mergeCollapse() { while (stackSize > 1) { int n = stackSize - 2; if (n > 0 && runLen[n-1] <= runLen[n] + runLen[n+1]) { if (runLen[n - 1] < runLen[n + 1]) n--; mergeAt(n); } else if (runLen[n] <= runLen[n + 1]) { mergeAt(n); } else { break; // Invariant is established } } }
总结,1.7之前的排序更易懂,插入+归并,数组长度低于7直接插入排序,
1.7开始使用TimSort+二分插入,数组长度小于32,直接二分插入排序
TimSort的平均时间复杂度和最坏时间复杂度和归并一样O(nlogn),但是其最好情况能达到O(n)
有理解错误的地方欢迎大家指出,后面有时间会看一下 java9的 sort实现方式做了哪些优化