排序是所有变成语言中都会有的部分,常见的排序方式有冒泡、快速、插入三种,那么Java语言本身使用的排序方式是哪种呢?
下面我们就来研究下Java源代码内集合(List)以及数组(Array)的排序方式。
首先从Collections的排序方法说起,Collections对List类型的排序方法sort有两种,一种是使用Java内部的排序规则(自然排序,根据ASCII码的大小),另一种是使用用户自定义的实现Comparator接口的排序类来定义排序规则。
打开Collections工具类的源代码:
sort(List,Comparator)方法:
public static <T> void sort(List<T> list, Comparator<? super T> c) {
Object[] a = list.toArray();
Arrays.sort(a, (Comparator)c);
ListIterator i = list.listIterator();
for (int j=0; j<a.length; j++) {
i.next();
i.set(a[j]);
}
}
sort(List)方法:
public static <T extends Comparable<? super T>> void sort(List<T> list) {
Object[] a = list.toArray();
Arrays.sort(a);
ListIterator<T> i = list.listIterator();
for (int j=0; j<a.length; j++) {
i.next();
i.set((T)a[j]);
}
}
我们发现Collections中的两个sort方法惊人的相似,都是先将要排序的List转化为数组,然后调用Arrays类中相应的sort方法排序,再然后得到List对象的迭代器,最后迭代List并将排序后的数组按序赋值到List中。
综上,我们可以得知Collections中的排序方式落实在Arrays类。
接下来,查看Arrays类中的sort类,代码就不贴出了,自己查看即可,最后的排序实现类是Arrays类中的mergeSort(原数组,目标数组,目标数组起始位置,目标数组的长度,偏移量)。
最最后,排序的实现重点在Arrays类中的mergeSort方法,上代码注释:
/**
* @Title: Java中List与Array的核心排序方法
* @param @param src 原数组
* @param @param dest 目标数组,这里的目标数组是原数组的clone
* @param @param low 目标数组中排序存放开始的索引
* @param @param high 目标数组中排序存放结束的索引
* @param @param off 原数组中生成对应的偏移量
* @return void
* @throws
*/
private static void mergeSort(Object[] src, Object[] dest, int low,
int high, int off) {
//得到目标数组存放排序结果的长度
int length = high - low;
//使用插入排序方式对目标数组中存放排序结果的部分排序
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;
}
//利用递归,不断的二分数组,直到数组的长度小于INSERTIONSORT_THRESHOLD(默认为7)则使用插入排序排序
int destLow = low;
int destHigh = high;
low += off;
high += off;
//利用无符号右移的方式得到中间位置的索引
int mid = (low + high) >>> 1;
//由于dest数组是src数组的clone,所以无论几遍的dest与src交换都能保证排序的部分不重复
mergeSort(dest, src, low, mid, -off);
mergeSort(dest, src, mid, high, -off);
//如果说二分后的两个部分恰好前部分都小于后部分,那么只需要将原数组中的数据从low起始的部分拷贝到desc中destLow起始的部分,拷贝长度为length
if (((Comparable) src[mid - 1]).compareTo(src[mid]) <= 0) {
//这里调用了native方法,高效、快速
System.arraycopy(src, low, dest, destLow, length);
return;
}
//将两个排好序的部分合成到一起
//这里就是将数组中分开的两部分,从两部分的起始位置也就是索引为0和中间值的两个值比较大小后,依次填充到目标数组中
//每次填充一个值,两部分填充过的部分索引+1,直到完全合并
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++];
}
}
对于实现Comparator接口,即用户自定义排序规则的方式,就是简单的将以上方法中插入排序部分中的使用默认的Comparable部分使用用户定义的排序规则来排序。
通过以上分析以及对代码的查看,我们了解到Java内部的排序是对大数组使用合并排序进行递归分解,直到分解为目标长度的小数组为止,然后小数组内部使用插入排序进行排序,最后还是合并排序进行合并。