ArrayList,排序方法的調用過程java
// 排序方法 public void sort(Comparator<? super E> c) { final int expectedModCount = modCount; Arrays.sort((E[]) elementData, 0, size, c); if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } modCount++; } public static <T> void sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c) { // 若是沒有實現比較方法 if (c == null) { sort(a, fromIndex, toIndex); } else { rangeCheck(a.length, fromIndex, toIndex); if (Arrays.LegacyMergeSort.userRequested) legacyMergeSort(a, fromIndex, toIndex, c); else TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0); } } public static void sort(Object[] a, int fromIndex, int toIndex) { rangeCheck(a.length, fromIndex, toIndex); //經查資料,這是個傳統的歸併排序,須要經過設置系統屬性後,才能進行調用 // System.setProperty("java.util.Arrays.useLegacyMergeSort", "true"); if (Arrays.LegacyMergeSort.userRequested) legacyMergeSort(a, fromIndex, toIndex); else ComparableTimSort.sort(a, fromIndex, toIndex, null, 0, 0); }
而後繼續看下在沒有實現Comparator接口的狀況,傳統歸併排序的實現數組
private static void legacyMergeSort(Object[] a, int fromIndex, int toIndex) { // 複製對應範圍的數組 Object[] aux = copyOfRange(a, fromIndex, toIndex); mergeSort(aux, a, fromIndex, toIndex, -fromIndex); } // 使用插入排序進行優化的歸併排序 // dest爲要排序的數組 // off:負數,由於aux是複製a數組fromIndex-toIndex範圍的數據,但位置從0開始,fromIndex-off則爲aux開始的座標 private static void mergeSort(Object[] src, Object[] dest, int low, int high, int off) { int length = high - low; // Insertion sort on smallest arrays // 長度小於7,則使用插入排序 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; // 交換dest,src位置,這樣就能排序src,而後合併到dest // 注意這個遞歸的開始,dest爲要排序的數組a,因此最終a會合併成有序的數組 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. // 若是說src排完序後,兩個範圍的src,恰好前一個小於後一個,則直接複製。 // 例如兩個範圍【1,2】【3,4】,其實他們已經有序【1,2,3,4】 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++]; } } /** * 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; }
再看看當前默認使用的排序方法(沒使用Comparable的狀況)ide
/** * * @param a 待排序的數組 * @param lo 開始位置,包括當前位置 * @param hi 結束位置,不包括當前位置 * @param work * @param workBase * @param workLen */ static void sort(Object[] a, int lo, int hi, Object[] work, int workBase, int workLen) { assert a != null && lo >= 0 && lo <= hi && hi <= a.length; 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 // 排序的範圍小於32的狀況 if (nRemaining < MIN_MERGE) { // 從lo位置開始,返回最長的遞增序列長度,(下面有介紹) 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, work, workBase, workLen); 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; }
具體看看裏面的方法實現學習
/** * 返回從lo開始最長遞增序列 * @param a 待排序的數組 * @param lo 開始位置,包括當前位置 * @param hi 結束位置,不包含當前位置 * @return */ 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 // 若是lo+1位置的數小於lo位置的數,就找出最長的遞減序列,而後進行反轉 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 reverseRange(Object[] a, int lo, int hi) { hi--; while (lo < hi) { Object t = a[lo]; a[lo++] = a[hi]; a[hi--] = t; } } /** * 折半插入排序 * @param a 待排序的數組 * @param lo 開始位置,包括當前位置 * @param hi 結束位置,不包含當前位置 * @param start start之前爲遞增序列 */ private static void binarySort(Object[] a, int lo, int hi, int start) { assert lo <= start && start <= hi; if (start == lo) start++; // 從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). */ // 二分找lo到start(不包括start)範圍內,找到pivot適合插入的位置 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 // 若是隻需後移2位或者1位,直接換值 // 更多,則調用系統方法進行復制 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; } }
沒分析完。。。須要學習一波tim sort。。優化