Arrays.sort()的底层实现
1.基本类型(以int为例)
源码中的快速排序,主要做了以下几个方面的优化:
1)当待排序的数组中的元素个数较少时,源码中的阀值为7,采用的是插入排序。尽管插入排序的时间复杂度为0(n^2),但是当数组元素较少时,插入排序优于快速排序,因为这时快速排序的递归操作影响性能。
2)较好的选择了划分元(基准元素)。能够将数组分成大致两个相等的部分,避免出现最坏的情况。例如当数组有序的的情况下,选择第一个元素作为划分元,将使得算法的时间复杂度达到O(n^2).
3)根据划分元 v ,形成不变式 v* (
源码中选择划分元的方法:
1)当数组大小为 size=7 时 ,取数组中间元素作为划分元。int n=m>>1;(此方法值得借鉴)。
2)当数组大小size大于7小于等于40时,取首、中、末三个元素中间大小的元素作为划分元。
3)当数组大小 size>40 时 ,从待排数组中较均匀的选择9个元素,选出一个伪中数做为划分元。
普通的快速排序算法,经过一次划分后,将划分元排到素组较中间的位置,左边的元素小于划分元,右边的元素大于划分元,而没有将与划分元相等的元素放在其附近,这一点,在Arrays.sort()中得到了较大的优化。
举例:15、93、15、41、6、15、22、7、15、20
举例:15、93、15、41、6、15、22、7、15、20
因size大于7小于等于40 ,所以在15、6、和20 中选择v = 15 作为划分元。
经过一次换分后: 15、15、7、6、41、20、22、93、15、15. 与划分元相等的元素都移到了素组的两边。
接下来将与划分元相等的元素移到数组中间来,形成:7、6、15、15、15、15、41、20、22、93.
最后递归对两个区间进行排序[7、6]和[41、20、22、93].,所以在15、6、和20 中选择v = 15 作为划分元。
2.Object类型
优化的归并排序既快速(nlog(n))又稳定。
对于对象的排序,稳定性很重要。比如成绩单,一开始可能是按人员的学号顺序排好了的,现在让我们用成绩排,那么你应该保证,本来张三在李四前面,即使他们成绩相同,张三不能跑到李四的后面去。
而快速排序是不稳定的,而且最坏情况下的时间复杂度是O(n^2)。
另外,对象数组中保存的只是对象的引用,这样多次移位并不会造成额外的开销,但是,对象数组对比较次数一般比较敏感,有可能对象的比较比单纯数的比较开销大很多。归并排序在这方面比快速排序做得更好,这也是选择它作为对象排序的一个重要原因之一。
排序优化:实现中快排和归并都采用递归方式,而在递归的底层,也就是待排序的数组长度小于7时,直接使用冒泡排序,而不再递归下去。
分析:长度为6的数组冒泡排序总比较次数最多也就1+2+3+4+5+6=21次,最好情况下只有6次比较。而快排或归并涉及到递归调用等的开销,其时间效率在n较小时劣势就凸显了,因此这里采用了冒泡排序,这也是对快速排序极重要的优化。
快排部分代码如下:
package com.util;
public class ArraysPrimitive {
private ArraysPrimitive() {}
/**
* 对指定的 int 型数组按数字升序进行排序。
*/
public static void sort(int[] a) {
sort1(a, 0, a.length);
}
/**
* 对指定 int 型数组的指定范围按数字升序进行排序。
*/
public static void sort(int[] a, int fromIndex, int toIndex) {
rangeCheck(a.length, fromIndex, toIndex);
sort1(a, fromIndex, toIndex - fromIndex);
}
private static void sort1(int x[], int off, int len) {
/*
* 当待排序的数组中的元素个数小于 7 时,采用插入排序 。
*
* 尽管插入排序的时间复杂度为O(n^2),但是当数组元素较少时, 插入排序优于快速排序,因为这时快速排序的递归操作影响性能。
*/
if (len < 7) {
for (int i = off; i < len + off; i++)
for (int j = i; j > off && x[j - 1] > x[j]; j--)
swap(x, j, j - 1);
return;
}
/*
* 当待排序的数组中的元素个数大于 或等于7 时,采用快速排序 。
*
* Choose a partition element, v
* 选取一个划分元,V
*
* 较好的选择了划分元(基准元素)。能够将数组分成大致两个相等的部分,避免出现最坏的情况。例如当数组有序的的情况下,
* 选择第一个元素作为划分元,将使得算法的时间复杂度达到O(n^2).
*/
// 当数组大小为size=7时 ,取数组中间元素作为划分元。
int m = off + (len >> 1);
// 当数组大小 7<size<=40时,取首、中、末 三个元素中间大小的元素作为划分元。
if (len > 7) {
int l = off;
int n = off + len - 1;
/*
* 当数组大小 size>40 时 ,从待排数组中较均匀的选择9个元素,
* 选出一个伪中数做为划分元。
*/
if (len > 40) {
int s = len / 8;
l = med3(x, l, l + s, l + 2 * s);
m = med3(x, m - s, m, m + s);
n = med3(x, n - 2 * s, n - s, n);
}
// 取出中间大小的元素的位置。
m = med3(x, l, m, n); // Mid-size, med of 3
}
//得到划分元V
int v = x[m];
// Establish Invariant: v* (<v)* (>v)* v*
int a = off, b = a, c = off + len - 1, d = c;
while (true) {
while (b <= c && x[b] <= v) {
if (x[b] == v)
swap(x, a++, b);
b++;
}
while (c >= b && x[c] >= v) {
if (x[c] == v)
swap(x, c, d--);
c--;
}
if (b > c)
break;
swap(x, b++, c--);
}
// Swap partition elements back to middle
int s, n = off + len;
s = Math.min(a - off, b - a);
vecswap(x, off, b - s, s);
s = Math.min(d - c, n - d - 1);
vecswap(x, b, n - s, s);
// Recursively sort non-partition-elements
if ((s = b - a) > 1)
sort1(x, off, s);
if ((s = d - c) > 1)
sort1(x, n - s, s);
}
/**
* Swaps x[a] with x[b].
*/
private static void swap(int x[], int a, int b) {
int t = x[a];
x[a] = x[b];
x[b] = t;
}
/**
* Swaps x[a .. (a+n-1)] with x[b .. (b+n-1)].
*/
private static void vecswap(int x[], int a, int b, int n) {
for (int i=0; i<n; i++, a++, b++)
swap(x, a, b);
}
/**
* Returns the index of the median of the three indexed integers.
*/
private static int med3(int x[], int a, int b, int c) {
return (x[a] < x[b] ? (x[b] < x[c] ? b : x[a] < x[c] ? c : a)
: (x[b] > x[c] ? b : x[a] > x[c] ? c : a));
}
/**
* Check that fromIndex and toIndex are in range, and throw an
* appropriate exception if they aren't.
*/
private static void rangeCheck(int arrayLen, int fromIndex, int toIndex) {
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex
+ ") > toIndex(" + toIndex + ")");
if (fromIndex < 0)
throw new ArrayIndexOutOfBoundsException(fromIndex);
if (toIndex > arrayLen)
throw new ArrayIndexOutOfBoundsException(toIndex);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
归并部分代码如下:
package com.util;
import java.lang.reflect.Array;
public class ArraysObject {
private static final int INSERTIONSORT_THRESHOLD = 7;
private ArraysObject() {}
public static void sort(Object[] a) {
//java.lang.Object.clone(),理解深表复制和浅表复制
Object[] aux = (Object[]) a.clone();
mergeSort(aux, a, 0, a.length, 0);
}
public static void sort(Object[] a, int fromIndex, int toIndex) {
rangeCheck(a.length, fromIndex, toIndex);
Object[] aux = copyOfRange(a, fromIndex, toIndex);
mergeSort(aux, a, fromIndex, toIndex, -fromIndex);
}
/**
* Src is the source array that starts at index 0
* Dest is the (possibly larger) array destination with a possible offset
* low is the index in dest to start sorting
* high is the end index in dest to end sorting
* off is the offset to generate corresponding low, high in src
*/
private static void mergeSort(Object[] src, Object[] dest, int low,
int high, int off) {
int length = high - low;
// Insertion sort on smallest arrays
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;
/*
* >>>:无符号右移运算符
* expression1 >>> expresion2:expression1的各个位向右移expression2
* 指定的位数。右移后左边空出的位数用0来填充。移出右边的位被丢弃。
* 例如:-14>>>2; 结果为:1073741820
*/
int mid = (low + high) >>> 1;
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.
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++];
}
}
/**
* Check that fromIndex and toIndex are in range, and throw an appropriate
* exception if they aren't.
*/
private static void rangeCheck(int arrayLen, int fromIndex, int toIndex) {
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex
+ ") > toIndex(" + toIndex + ")");
if (fromIndex < 0)
throw new ArrayIndexOutOfBoundsException(fromIndex);
if (toIndex > arrayLen)
throw new ArrayIndexOutOfBoundsException(toIndex);
}
public static <T> T[] copyOfRange(T[] original, int from, int to) {
return copyOfRange(original, from, to, (Class<T[]>) original.getClass());
}
public static <T, U> T[] copyOfRange(U[] original, int from, int to,
Class<? extends T[]> newType) {
int newLength = to - from;
if (newLength < 0)
throw new IllegalArgumentException(from + " > " + to);
T[] copy = ((Object) newType == (Object) Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, from, copy, 0,
Math.min(original.length - from, newLength));
return copy;
}
/**
* 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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
辅助理解
package com.lang;
public final class System {
//System 类不能被实例化。
private System() {}
//在 System 类提供的设施中,有标准输入、标准输出和错误输出流;对外部定义的属性
//和环境变量的访问;加载文件和库的方法;还有快速复制数组的一部分的实用方法。
/**
* src and dest都必须是同类型或者可以进行转换类型的数组.
* @param src the source array.
* @param srcPos starting position in the source array.
* @param dest the destination array.
* @param destPos starting position in the destination data.
* @param length the number of array elements to be copied.
*/
public static native void arraycopy(Object src, int srcPos, Object dest,
int destPos, int length);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.lang.reflect;
public final class Array {
private Array() {}
//创建一个具有指定的组件类型和维度的新数组。
public static Object newInstance(Class<?> componentType, int length)
throws NegativeArraySizeException {
return newArray(componentType, length);
}
private static native Object newArray(Class componentType, int length)
throws NegativeArraySizeException;
}