有关Arrary.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);
}
}
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)
return; // 当数组长度小于等于2时说明已经是排好序的了。
// 如果数组长度小于32则使用二分查找加插入排序。
if (nRemaining < MIN_MERGE) {
//initRunLen是数组从左到右的最大长度的有序序列长度。内部查找,如果是顺序则正常返回。逆序时将逆序部分反转成顺序。
int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
binarySort(a, lo, hi, lo + initRunLen, c);
return;
}
大于等于32算出合适分区大小,按照升序降序特点进行分区。其中还包括排除对已经具有顺序的排序。对数据merge排序是二路归并,最下边是两个独立的数进行排序,而TimSort是多路归并。多个数是一个分区,一个分区就是所谓的run,将两个run合并成一个run,合并结果存栈,将栈内的run合并为一个。
分区的例子:假设有一个数组,1,2,3,6,4,5,8,6,4 ,根据寻找连续升序、降序规则划分分区结果为
第一个升序或降序是{1,2,3,6},第二个升序或降序为{4,5,8},第三个升序或降序为{6,4},因为是降序所以反转为{4,6}
第一个区块长度为4,即runLen;pivot是3;(因为数组是从零开始计数,第二个分区的值是四到八,从大数组的下标为三的位置开始。)
/**
* 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.
*/
//当数组长度大于32时
TimSort<T> ts = new TimSort<>(a, c, work, workBase, workLen);
//这部分是根据数组长度计算出最小分区是多少。返回一个小于32的数,比如33
由于33为奇数并且大于32所以返回16+1 如果是32返回16
int minRun = minRunLength(nRemaining);
do {
// 计算原始最大顺序序列。
int runLen = countRunAndMakeAscending(a, lo, hi, c);
// 如果顺序序列小于最小执行长度则通过二分插入排序使其达到最小执行长度,否则存进ts集合。
if (runLen < minRun) {
//force作用是当nRmaining小于则使用nRmianing,否则使用最小minRun
//返回nRmaining情况是循环执行最后只剩下nRmianing。
int force = nRemaining <= minRun ? nRemaining : minRun;
//lo+force代表分区长度,lo+runLen代表顺序序列的最大长度
binarySort(a, lo, lo + force, lo + runLen, c);
runLen = force;
}
//这一步将每一段长度大于有序序列分区的开头结束位置存入ts集合中
ts.pushRun(lo, runLen);
//这一步将两个有序分区进行合并
ts.mergeCollapse();
//对下一部分无序区进行操作
lo += runLen;
nRemaining -= runLen;
} while (nRemaining != 0);
// 最后进行归并,因为当循环结束后最后一段分区并没有执行 ts.mergeCollapse()语句。
assert lo == hi;
ts.mergeForceCollapse();
assert ts.stackSize == 1;
}
private static <T> void binarySort(T[] a, int lo, int hi, int start,
Comparator<? super T> c) {
assert lo <= start && start <= hi;
//如果数组从左到右没有顺序序列,则将第一个数作为有序序列
if (start == lo)
start++;
for ( ; start < hi; start++) {
//保存第一个数
T pivot = 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).
*/
//通过二分查找的方式查找pivot在有序序列中正确的位置。
while (left < right) {
int mid = (left + right) >>> 1;
if (c.compare(pivot, 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.
*/
//判断移动位置次数是否小于2如果小于则不需要调用System.arraycopy函数
int n = start - left;
switch (n) {
//由于pivot已经将a[start]保存了所以可以后移
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 static int minRunLength(int n) {
assert n >= 0;
int r = 0; // Becomes 1 if any 1 bits are shifted off
while (n >= MIN_MERGE) {
r |= (n & 1);//当n为偶数时r = 0 当n为奇数时r = 1
n >>= 1;
}
return n + r;
}