# Collections.sort in JDK6：MergeSort

本文是对JDK6中Collections.sort方法的源码解析，也可以看作是对Comparison method violates its general contract!的后续分析。在JDK6中，该方法底层使用的是经过优化后的归并排序，废话不多说，直接看源码。

```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]);
}
}```

将list转成数组，然后调用Arrays.sort方法排序，最后将排好顺序的值覆盖到原list上。

```public static <T> void sort(T[] a, Comparator<? super T> c) {
T[] aux = (T[])a.clone();
if (c==null)
mergeSort(aux, a, 0, a.length, 0);
else
mergeSort(aux, a, 0, a.length, 0, c);
}```

克隆一个数组，如果比较器为空，mergeSort(aux, a, 0, a.length, 0);如果比较器不为空，mergeSort(aux, a, 0, a.length, 0, c);二者内部算法实现一致，只是比较元素的方法不一样。下面来看归并排序的实现，看其是如何优化的。

```private static void mergeSort(Object[] src,
Object[] dest,
int low, int high, int off,
Comparator c) {
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 && c.compare(dest[j-1], 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;
int mid = (low + high) >>> 1;
mergeSort(dest, src, low, mid, -off, c);
mergeSort(dest, src, mid, high, -off, c);

// 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 (c.compare(src[mid-1], 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 && c.compare(src[p], src[q]) <= 0)
dest[i] = src[p++];
else
dest[i] = src[q++];
}
}```

我们分段来看。

```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 && c.compare(dest[j-1], dest[j])>0; j--)
swap(dest, j, j-1);
return;
}```

这里有一个常量INSERTIONSORT_THRESHOLD。

```/**
* Tuning parameter: list size at or below which insertion sort will be
* used in preference to mergesort or quicksort.
*/
private static final int INSERTIONSORT_THRESHOLD = 7;```

当数组长度<7时，这里使用了直接插入排序。直接插入排序的过程可以看这个视频，插入排序适用于小数列的排序。这里是JDK6中归并排序的第一个优化

```// Recursively sort halves of dest into src
int destLow  = low;
int destHigh = high;
low  += off;
high += off;
int mid = (low + high) >>> 1;// 中间索引，相当于(low + high) / 2
mergeSort(dest, src, low, mid, -off, c);// 排序左边
mergeSort(dest, src, mid, high, -off, c);// 排序右边```

这里开始递归排序，我们不需要关注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 (c.compare(src[mid-1], src[mid]) <= 0) {
System.arraycopy(src, low, dest, destLow, length);
return;
}```

左边和右边排好序之后，开始合并。这时src[low ~ mid - 1]和src[mid ~ high - 1]都是有序的，这时比较src[mid - 1]和src[mid]，如果前者比后者小，那么皆大欢喜，真个src数组就是有序的了，只需将其复制到目标数组后，就完成了排序，不过这种碰运气的几率会比较小。这里是JDK6中归并排序的第二个优化

```// Merge sorted halves (now in src) into dest
for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
if (q >= high || p < mid && c.compare(src[p], src[q]) <= 0)
dest[i] = src[p++];
else
dest[i] = src[q++];
}```

程序执行到这里，进行传统的合并操作。其过程如下图：

初始状态：

循环一次后：

每次都比较src[p]和src[q]，将较小的元素存储到dest[i]，不断的循环比较，直至整个数组都有序。

最终：

JDK6中的排序是基于传统的归并排序做了部分优化，这两个优化都很简单，实际上效率并未提高多少。所以在JDK7中将其替换为TimSort，下回分解。

（完）

79 篇文章29 人订阅

0 条评论

## 相关文章

### C# 委托的一些使用上的小技巧

1、委托是一种数据类型,我们可以在任何定义类的地方定义委托,在任何声明类的地方声明委托 2、初始化委托有两种方式,代码如下: (1)、像类一样初始化委托 pub...

1896

### Linq快速入门——Lambda表达式的前世今生

Lambda表达式其实并不陌生，他的前生就是匿名函数，所以要谈Lambda表达式，就不得不谈匿名函数，要谈匿名函数，那又要不得不谈委托。 何为委托 匿名方法 ...

23010

### 初识json

JSON: JavaScript Object Notation，是一种数据格式，对，仅仅是一种格式。 和它类似的还有XML;

903

### LWC 61：738. Monotone Increasing Digits

LWC 61：738. Monotone Increasing Digits 传送门：738. Monotone Increasing Digits Probl...

1967

1361

511

1422

### .NET面试题解析(03)-string与字符串操作

4.以下代码执行后内存中会存在多少个字符串？分别是什么？输出结果是什么？为什么呢？

812

### (int),Int32.Parse,Convert.ToInt3…

(int)是一种被称为强制转换的显示转换。源变量和目标变量必须是兼容的（必须都是int类型的）。并且有丢失数据的风险。因为目标变量的类型大小小于源变量。

1083

2784