根哥源码学习笔记之Java Array.sort
sort是Arrays类中一个静态方法,此处用针对整数数组的方法,具体用法是将一个整数数组按照从小到大的顺序排列。方法里面直接指向DualPivotQuicksort方法。
public static void sort(int[] a) {
DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}
继续查看DualPivotQuicksort,茫茫多的代码,根据给出的输入条件可以找到
if (right - left < QUICKSORT_THRESHOLD) {
sort(a, left, right, true);
return;
}
向前查看QUICKSORT_THRESHOLD (286),发现这是一个阈值,当排序个数小于这个阈值的时候使用快排。输入了布尔值true,转到另一个重载的sort()方法,这时候又出现一个常量INSERTION_SORT_THRESHOLD(47),小于这个会进一步用到插入排序。
总之就是
**用插入排序< 47 < 用快排 < 286 **
插入排序JDK源码如下所示。这个写法看着有点难受,个人把ai变量改成tmp后,在用一个数列试了下,就感觉清爽多了。
for (int i = left, j = i; i < right; j = ++i) {
int ai = a[i + 1];
while (ai < a[j]) {
a[j + 1] = a[j];
if (j-- == left) {
break;
}
}
a[j + 1] = ai;
}
下图是个人用excel做出的插入排序的例子,每个阶段的i, j都是本次循环结束时候的样子,小白理解过程,尤其是边界条件必备。
大于INSERTION_SORT_THRESHOLD就可以使用快排了,下图是单轴快排的每个步骤,做出这个图后,快排已经进入我婶婶的脑海了。主要思想就是找第一个元素作为轴(pivot),小于这个元素的靠左,大于的靠右(通过把左边的大于pivot的元素和右边小于pivot的元素对调),再把pivot插入其中,轴左边和右边再分别进行新一轮快排。
public static void deScanSwapSort(int[] items, int start, int end) {
if (start < end) {
int pivot = items[start];
int i = start + 1, j = end;
while (i <= j) {
while (i <= j && items[i] < pivot)
i++;
while (i <= j && items[j] >= pivot)
j--;
if (i <= j) {
swap(items, i, j);
}
}
swap(items, start, j);// 将中心点交换到中间。
deScanSwapSort(items, start, j - 1);// 中心点左半部分递归
deScanSwapSort(items, j + 1, end);// 中心点右半部分递归
}
}
private static void swap(int[] items, int i, int j) {
int tmp = items[i];
items[i] = items[j];
items[j] = tmp;
}
}
原创图,转发请标明出处
前面说了一半,大于286个元素的情况下考虑使用归并排序,但是在使用前需要验证下当前数组是混乱型号还是有些排序基础的。下面的代码意思就是找到局部连续升序或者降序(如有,搞成升序)或相等元素的段数。当段数超过某一阈值MAX_RUN_COUNT时候,说明起伏大了,没什么结构,还是得用快排。但是如果段数没超过阈值,说明有些结构,直接使用归并排序。
for (int k = left; k < right; run[count] = k) {
if (a[k] < a[k + 1]) { // ascending
while (++k <= right && a[k - 1] <= a[k]);
} else if (a[k] > a[k + 1]) { // descending
while (++k <= right && a[k - 1] >= a[k]);
for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {
int t = a[lo]; a[lo] = a[hi]; a[hi] = t;
}
} else { // equal
for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) {
if (--m == 0) {
sort(a, left, right, true);
return;
}
}
}
if (++count == MAX_RUN_COUNT) {
sort(a, left, right, true);
return;
}
}
下图是归并排序,就是分治排序后再合并
for (int last; count > 1; count = last) {
for (int k = (last = 0) + 2; k <= count; k += 2) {
int hi = run[k], mi = run[k - 1];
for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {
if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {
b[i + bo] = a[p++ + ao];
} else {
b[i + bo] = a[q++ + ao];
}
}
run[++last] = hi;
}
if ((count & 1) != 0) {
for (int i = right, lo = run[count - 1]; --i >= lo;
b[i + bo] = a[i + ao]
);
run[++last] = right;
}
int[] t = a; a = b; b = t;
int o = ao; ao = bo; bo = o;
}
}
看完这个方法后弄明白了一个数组送进sort()之后要经历这么多过程。而且这里面的那些阈值之类的设定都是有论文作为依据的(注释中提到)。致敬开发者们!