一、底層使用TimSort,TimSort是什麼呢?
TimSort的核心思想是:先找出序列本身從0索引開始已排好的(遞增)序列(如果是遞減序列則將之翻轉爲遞增),再把剩下的元素進行排序;若序列很長,則把序列分段,重複上述操作,最後把若干段序列合併。
二、源碼詳解(留意註釋)
我們直奔重點,看sort方法(追蹤到ComparableTimSort.class中)
public static void sort(Object[] a) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a);
else
//底層調用此方法,a指要排序的數組(集合)
ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}
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;
// 數組長度小於2的,不用排
if (nRemaining < 2)
return; // Arrays of size 0 and 1 are always sorted
// 數組長度小於MIN_MERGE(32)的,使用"mini-TimSort"排序
if (nRemaining < MIN_MERGE) {
// 此方法是把數組中從索引0開始的最長的遞減序列找出,並將之按遞增重排;若從索引0開始的是遞增序列,則找出數組中最前的需要重排的索引。(即此方法執行完成後initRunLen索引前的元素順序已排好了。詳解可看“分1”)
int initRunLen = countRunAndMakeAscending(a, lo, hi);
// 從initRunLen索引開始,兩兩對比,把大的排後面
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.
*/
// 數組長度大於MIN_MERGE(32)
// 算法思想:分總。把序列分成若干段,每段處理:先找出已排好的,再使用mini-TimSort排剩下的。
// 最後再把若干段合併
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);
//至此,排好了0-force
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;
}
分1:
private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {
assert lo < hi;
int runHi = lo + 1;//1
if (runHi == hi)
return 1;
// Find end of run, and reverse range if descending
// 索引1和索引0的元素的對比
if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) { // Descending 如果是遞減序列
//索引2和索引1的元素的對比...以此類推
while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)
runHi++;
// 已找到最長遞減序列,將之排好序(詳情看“分2”)
reverseRange(a, lo, runHi);
} else { // Ascending 如果是遞增序列
while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) >= 0)
runHi++;
// 找出第一個非遞增序列的元素的索引
}
return runHi - lo;
}
分2:(把遞減序列重排爲遞增序列)
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;
}
}