Arrays提供了对基本数据类型、引用数据类型的排序功能。本文只讨论基本数据类型的数组排序。
上图是Arrays中的除了boolean类型的所有排序方法。下面以int[]为例进行展开。
点开sort()方法,会看到请求一下方法
public static void sort(int[] a) {
DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}
点击进入 DualPivotQuicksort.sort;我们会看到首先会进行了一次判断
/**
* Sorts the specified range of the array using the given
* workspace array slice if possible for merging
*
* @param a the array to be sorted
* @param left the index of the first element, inclusive, to be sorted
* @param right the index of the last element, inclusive, to be sorted
* @param work a workspace array (slice)
* @param workBase origin of usable space in work array
* @param workLen usable size of work array
*/
static void sort(int[] a, int left, int right,
int[] work, int workBase, int workLen) {
// Use Quicksort on small arrays
if (right - left < QUICKSORT_THRESHOLD) {
sort(a, left, right, true);
return;
}
。。。此处省略。。。
}
当数组长度小于QUICKSORT_THRESHOLD(286)时,会进入私有方法sort中进行排序后直接返回。否则往下。
而点击进入sort方法时,又会有一个判断
/**
* Sorts the specified range of the array by Dual-Pivot Quicksort.
*
* @param a the array to be sorted
* @param left the index of the first element, inclusive, to be sorted
* @param right the index of the last element, inclusive, to be sorted
* @param leftmost indicates if this part is the leftmost in the range
*/
private static void sort(int[] a, int left, int right, boolean leftmost) {
int length = right - left + 1;
// Use insertion sort on tiny arrays
if (length < INSERTION_SORT_THRESHOLD) {
if (leftmost) {
/*
* Traditional (without sentinel) insertion sort,
* optimized for server VM, is used in case of
* the leftmost part.
*/
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;
}
} else {
/*
* Skip the longest ascending sequence.
*/
do {
if (left >= right) {
return;
}
} while (a[++left] >= a[left - 1]);
/*
* Every element from adjoining part plays the role
* of sentinel, therefore this allows us to avoid the
* left range check on each iteration. Moreover, we use
* the more optimized algorithm, so called pair insertion
* sort, which is faster (in the context of Quicksort)
* than traditional implementation of insertion sort.
*/
for (int k = left; ++left <= right; k = ++left) {
int a1 = a[k], a2 = a[left];
if (a1 < a2) {
a2 = a1; a1 = a[left];
}
while (a1 < a[--k]) {
a[k + 2] = a[k];
}
a[++k + 1] = a1;
while (a2 < a[--k]) {
a[k + 1] = a[k];
}
a[k + 1] = a2;
}
int last = a[right];
while (last < a[--right]) {
a[right + 1] = a[right];
}
a[right + 1] = last;
}
return;
}
。。。省略。。。
}
1. 当数组长度小于INSERTION_SORT_THRESHOLD(47)时,这里会使用插入排序,这是我们在里面看到的第一个排序算法(这里需要说明的是,这里有个参数leftmost,初始进入的时候,leftmost=true,在递归调用此方法(sort)的时候,leftmost会有不同的传值)。
2. 当数组长度为INSERTION_SORT_THRESHOLD到QUICKSORT_THRESHOLD(不包含)之间时,又用到的是另外的排序算法。我们从注释里面可以看到,叫做Dual-Pivot Quicksort。其实就是一种快速排序的实现方式,只不过这里使用了轴元素pivot1,pivot2,将数组分成三段进行递归。这是我们在里面看到的第二种排序算法
3. 当数组长度超过QUICKSORT_THRESHOLD时,我们在注释里面可以看到merging,也就是归并排序的影子。但是真的是直接就使用了归并排序了吗,接下来我们可以进行分析一下。
在接下来的代码中,首先出现的是以下一段代码:
/*
* Index run[i] is the start of i-th run
* (ascending or descending sequence).
*/
int[] run = new int[MAX_RUN_COUNT + 1];
int count = 0; run[0] = left;
// Check if the array is nearly sorted
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;
}
}
}
/*
* The array is not highly structured,
* use Quicksort instead of merge sort.
*/
if (++count == MAX_RUN_COUNT) {
sort(a, left, right, true);
return;
}
}
文中有一句注释“Check if the array is nearly sorted”,校验数组是否趋近于有序。如果不是(The array is not highly structured),那么就使用前面提到的私有sort方法,用快速排序替代归并排序(use Quicksort instead of merge sort.),排序完成直接返回。否则往下走。
这里是什么意思呢?
这里定义一个常量MAX_RUN_COUNT = 67,一个计数器int count = 0,一个长度为MAX_RUN_COUNT + 1的数组,令run[0] = left,然后从传入数组的最左侧left开始遍历, 若数组的前n个元素均为升序/降序排列, 而第n + 1个元素的升/降序发生了改变, 则将第n个元素的索引存入run[1], 同时++count, 此时count的值为1;以此类推...
如果整个数组遍历完成,并且count小于MAX_RUN_COUNT ,则认为数组是高度结构化的,就认为是趋近于有序的。
然后接着往下看。
// Check special cases
// Implementation note: variable "right" is increased by 1.
if (run[count] == right++) { // The last run contains one element
run[++count] = right;
} else if (count == 1) { // The array is already sorted
return;
}
如果count == 1,则整个数组遍历完了,升序/降序序列都没有发生改变,则认为数组是有序的,直接返回,否则往下,就是最终的归并排序了
总结一下:
这里有三个常量:
QUICKSORT_THRESHOLD = 286
INSERTION_SORT_THRESHOLD = 47
MAX_RUN_COUNT = 67
1. 如果数组长度 < 47,则进行插入排序
2. 如果数组长度 ≥ 47 且 < 286,则进行快速排序(两个Pivot)
3. 如果数组长度 ≥ 286,如果数组趋近于有序,使用步骤2的排序方法,否则使用归并排序
注: 具体步骤自行debug调试,本文只是个人观点。