Java中Collections与Arrays的排序解析

排序是所有变成语言中都会有的部分,常见的排序方式有冒泡、快速、插入三种,那么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内部的排序是对大数组使用合并排序进行递归分解,直到分解为目标长度的小数组为止,然后小数组内部使用插入排序进行排序,最后还是合并排序进行合并。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值