where
Arrays和Collections 是JDK 中能实现排序的两个工具类,都来自java.util工具包
what
Arrays.sort 主要用于数组排序,包括基本类型的数组和对象数组。 Collections.sort 对List等集合类进行排序.
底层实现(jdk1.8版本)
Collections.sort 方法调用了list.sort方法
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
list.sort方法 调用了Arrays.sort 方法
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
Collections.sort方法底层调用的Arrays.sort方法,代码如下
public static <T> void sort(T[] a, Comparator<? super T> c) {
if (c == null) {
sort(a);
} else {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, c);
else
TimSort.sort(a, 0, a.length, c, null, 0, 0);
}
}
如果比较器 c 为null,进入sort(a)方法。如下:
public static void sort(Object[] a) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a);
else
ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}
legacyMergeSort(a),归并排序,
ComparableTimSort.sort():即Timsort排序。
Arrays.sort底层实现
除了以上Collections.sort 中 调用的arrays.sort方法,还有
Arrays.sort()针对基本类型数组和对象数组采用不同的排序方法:基本类型数组采用DualPivotQuicksort.sort(),快速排序的一种优化:双枢轴快速排序。
public static void sort(int[] a) {
DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}
对象数组采用legacyMergeSort() 归并排序和 ComparableTimSort.sort() TimSort 排序。
public static void sort(Object[] a) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a);
else
ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}
/** To be removed in a future release. */
private static void legacyMergeSort(Object[] a) {
Object[] aux = a.clone();
mergeSort(aux, a, 0, a.length, 0);
}
LegacyMergeSort.userRequested这个bool值是什么呢?
跟踪这个值,我们发现有这样的一段定义:
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.
一种老的归并排序,现在默认是关的。
对对象进行排序,没有采用快速排序,原因:快速排序不稳定,TimSort是稳定的。
对于基本类型,由于不要求排序是稳定的,因此使用了平均效率最好的排序算法——快速排序
扩展
java.util.Arrays类中的一些列重载的sort的方法为给定数组进行排序,以下是各个重载方法:
综上:对于Arrays类中的sort方法,根据第一个参数的类型,可以将这些方法分为3类:
- 基本数据类型数组的数值排序方法:提供了对于byte,short,int,long,char,float,double这些基本数据类型的数组的排序方法;
- 自定义类的对象数组排序方法:提供了对于任意类类型个数组的排序方法,这些方法的输入参数为Object数组;
- 泛型方法提供了泛型对象数组的排序实现。
jdk 5
基本类型排序:优化的快速排序 ;
对象数组的排序:经过修改的归并排序
jdk1.7
DualPivotQuickSort 是jdk 1.7 开始采用的双枢轴快速排序,这种快速排序算法,相对于传统的单Pivot的快速排序效率要更好.
TimSort 是 jdk 1.7 开始采用的,它是一种二分插入排序和归并排序的变种算法
过程
两个阀值
286,47.
宏观
数据规模<47 使用插入排序,47<=数据规模<286 用双枢轴快速排序,数据规模>=286 用TimSort 排序
细节
先判断数据规模是否 < 286,
否:TimSort排序:判断数组是否具备结构(按升序或降序寻找run, run个数>67, 无结构,用双枢轴快排,<67 有结构,合并run栈(归并排序))
是:再判断数据规模是否<47, 是: 插入排序 否: 双枢轴快速排序
https://www.imooc.com/article/45462
https://my.oschina.net/hosee/blog/652280
双枢轴快速排序原理
双基准快排(DualPivotQuicksort),顾名思义有两个轴元素pivot1,pivot2,且pivot≤pivot2,将序列分成三段:x < pivot1、pivot1 ≤ x ≤ pivot2、x>pivot2,然后分别对三段进行递归,这个算法通常会比传统的快排效率更高
归并排序原理
TimSort 原理
找到数据中已经排好序的块-分区,每一个分区叫一个run,然后按规则合并这些run。
Timsort的核心过程
TimSort 算法为了减少对升序部分的回溯和对降序部分的性能倒退,将输入按其升序和降序特点进行了分区。排序的输入的单位不是一个个单独的数字,而是一个个的块-分区。其中每一个分区叫一个run。针对这些 run 序列,每次拿一个 run 出来按规则进行合并。每次合并会将两个 run合并成一个 run。合并的结果保存到栈中。合并直到消耗掉所有的 run,这时将栈上剩余的 run合并到只剩一个 run 为止。这时这个仅剩的 run 便是排好序的结果。
综上述过程,Timsort算法的过程包括
(0)如何数组长度小于某个值,直接用二分插入排序算法
(1)找到各个run,并入栈
(2)按规则合并run