Collections.sort方法底层就是调用的Arrays.sort方法。
写一个例子看源码:
public static void main(String[] args) {
List
Collections.sort(strings);//sort方法在这里
for (String string : strings) {
System.out.println(string);
}
}
跟踪代码,发现Collections.sort调用的是list.sort方法:
@SuppressWarnings("unchecked")
public static
list.sort(null);
}
继续跟踪,发现是用Arrays.sort:
@SuppressWarnings({"unchecked", "rawtypes"})
default void sort(Comparator super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator
for (Object e : a) {
i.next();
i.set((E) e);
}
}
发现调用的是Arrays.sort(a,c):
public static
if (c == null) {
sort(a);
} else {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, c);
else
TimSort.sort(a, 0, a.length, c, null, 0, 0);
}
}
没有写比较器c,跟踪LegacyMergeSort,结果如下:
/**
* Old merge sort implementation can be selected (for
* compatibility with broken comparators) using a system property.
* Cannot be a static boolean in the enclosing class due to
* circular dependencies. To be removed in a future release.
*/
static final class LegacyMergeSort {
private static final boolean userRequested =
java.security.AccessController.doPrivileged(
new sun.security.action.GetBooleanAction(
"java.util.Arrays.useLegacyMergeSort")).booleanValue();
}
是一种老的归并排序,我们走的是下面TimSort.sort()这个方法:
static
T[] work, int workBase, int workLen) {
assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;
int nRemaining = hi - lo;
if (nRemaining < 2)
return; // Arrays of size 0 and 1 are always sorted
// If array is small, do a "mini-TimSort" with no merges
if (nRemaining < MIN_MERGE) {
int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
binarySort(a, lo, hi, lo + initRunLen, c);
return;
}
/**
* March over the array once, left to right, finding natural runs,
* extending short natural runs to minRun elements, and merging runs
* to maintain stack invariant.
*/
TimSort
int minRun = minRunLength(nRemaining);
do {
// Identify next run
int runLen = countRunAndMakeAscending(a, lo, hi, c);
// If run is short, extend to min(minRun, nRemaining)
if (runLen < minRun) {
int force = nRemaining <= minRun ? nRemaining : minRun;
binarySort(a, lo, lo + force, lo + runLen, c);
runLen = force;
}
// Push run onto pending-run stack, and maybe merge
ts.pushRun(lo, runLen);
ts.mergeCollapse();
// Advance to find next run
lo += runLen;
nRemaining -= runLen;
} while (nRemaining != 0);
// Merge all remaining runs to complete sort
assert lo == hi;
ts.mergeForceCollapse();
assert ts.stackSize == 1;
}
不论是Collections.sort方法或者是Arrays.sort方法,底层实现都是TimSort实现的,这是jdk1.7新增的,以前是归并排序。TimSort算法就是找到已经排好序数据的子序列,然后对剩余部分排序,然后合并起来。
Colletions.sort(list) 与 Arrays.sort(T[])
Colletions.sort()实际会将list转为数组,然后调用Arrays.sort(),排完了再转回List。
PS. JDK8里,List有自己的sort()方法了,像ArrayList就直接用自己内部的数组来排,而LinkedList, CopyOnWriteArrayList还是要复制出一份数组。
而Arrays.sort(),对原始类型(int[],double[],char[],byte[]),JDK6里用的是快速排序,对于对象类型(Object[]),JDK6则使用归并排序。为什么要用不同的算法呢?
JDK7的进步
到了JDK7,快速排序升级为双基准快排(双基准快排vs三路快排);归并排序升级为归并排序的改进版TimSort,一个JDK的自我进化。(TimSort例子http://xieyan87.com/2015/08/timsort%E7%AE%97%E6%B3%95%E7%AE%80%E4%BB%8B/)
JDK8的进步
再到了JDK8, 对大集合增加了Arrays.parallelSort()函数,使用fork-Join框架,充分利用多核,对大的集合进行切分然后再归并排序,而在小的连续片段里,依然使用TimSort与DualPivotQuickSort。
TimSort:
Timsort是一种结合了归并排序和插入排序的混合算法,由Tim Peters在2002年提出,并且已经成为Python 2.3版本以后内置排序算法,并且Java SE 7, Android平台,GNU Octave也引入了这一排序算法。简单来说,这个算法可以概括为两步:
1) 第一步就是把待排数组划分成一个个run,当然run不能太短,如果长度小于minrun这个阈值,则用插入排序进行扩充;
2) 第二步将run入栈,当栈顶的run的长度满足:runLen[n-2] <= runLen[n-1] + runLen[n]或者 runLen[n-1] <= runLen[n], 则对两个短run归并为一个新run,则到只剩栈顶元素时排序也完成了。