Arrays.sort源码阅读
Java数组和List对象的排序方法分别是Arrays.sort(T[] a)和Collections.sort(List
Arrays.sort方法源码很简单,对于基本数据类型的就是调用了DualPivotQuicksort.sort方法,对于对象类型的调用归并排序。
DualPivotQuicksort.sort()方法的流程
如果长度小于
QUICKSORT_THRESHOLD(286)
,则采用非归并排序如果长度小于
INSERTION_SORT_THRESHOLD(47)
,则采用插入排序- 最左区间(以初始left开始的区间)
leftmost
:普通插入排序 - 否则:
pair insertion sort
- 最左区间(以初始left开始的区间)
否则,快速排序
将数组划分为
7
段(大约),然后找出第2、3、4、5、6
段的右端点对应的位置对这
5
个位置上的数字进行插入排序,作为枢轴的候选如果
5
个数都不相等选取排序后的
2、4
作为枢轴,进行双枢轴排序Dual-Pivot Quicksort
效果:
1
2
3
4left part center part right part
+----------------------------------------------------------+
| == pivot1 | pivot1 < && < pivot2 | ? | == pivot2 |
+----------------------------------------------------------+排序后如果,中间部分元素过多,可能原因是等于pivort1和等于pivort2的元素过多,则将其调整为:
1
2
3
4left part center part right part
+----------------------------------------------------------+
| == pivot1 | pivot1 < && < pivot2 | ? | == pivot2 |
+----------------------------------------------------------+
否则,进行普通快速排序,枢轴排序后为第3个元素
否则,考虑
Timsort
(归并排序的优化版本,对一会升序、一会降序的混合情况处理比较好)创建
Timsort run
数组,大小为MAX_RUN_COUNT(67) + 1
a[run[i]] ~ a[run[i + 1]]
之间为升序数组检查当前待排序数组是否适合使用
Timsort
,即run
数组中升序数组个数,如果个数不小于MAX_RUN_COUNT
则认为数组内元素排序比较混乱,适合非归并排序
- 注:对于连续下降的元素会将其调整为连续上升
如果通过上述检测,则进行归并排序
DualPivotQuicksort源码阅读
1 |
|
那么Arrays.sort()是不是稳定的呢?
下面内容引用自 Java 官方文档。
双轴快速排序(快排不是稳定的排序)
public static void sort(int[] a, int fromIndex, int toIndex)
The sorting algorithm is a Dual-Pivot Quicksort by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm offers O(n log(n)) performance on all data sets, and is typically faster than traditional (one-pivot) Quicksort implementations.
之所以基本类型使用快速排序,因为对于基本类型来说,比较函数不支持自定义的比较器,因此在常规顺序下,是否稳定排序是结果上没差别的,就直接快速排序了。
归并排序(稳定的排序)
public static void sort(Object[] a, int fromIndex, int toIndex)
Implementation note: This implementation is a stable, adaptive, iterative mergesort that requires far fewer than n lg(n) comparisons when the input array is partially sorted, while offering the performance of a traditional mergesort when the input array is randomly ordered. If the input array is nearly sorted, the implementation requires approximately n comparisons. Temporary storage requirements vary from a small constant for nearly sorted input arrays to n/2 object references for randomly ordered input arrays.
稳定的,自适应的,迭代的归并排序,而且在数组部分有序的时候,需要的比较次数远小于 $O\left(n\log n\right)$。
Java归并排序的算法实现:
1 | /** To be removed in a future release. */ |
public static <T extends Comparable<? super T>> void sort(List<T> list)
Sorts the specified list into ascending order, according to the natural ordering of its elements. All elements in the list must implement the Comparable interface. Furthermore, all elements in the list must be mutually comparable (that is, e1.compareTo(e2) must not throw a ClassCastException for any elements e1 and e2 in the list).
This sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort.
The specified list must be modifiable, but need not be resizable.
这里没有明确说明排序算法,因为 List 有不同的 implementation,如果是 ArrayList,则是调用 Arrays.sort 来使用 TimSort 或者 LegacyMergeSort;如果是 LinkedList,则是将内部转为 Object[] 后再调用 Arrays.sort,总之都是稳定的。以下两个代码片段,前者是 LinkedList,后者是 ArrayList。
Collections.sort()
1 | default void sort(Comparator<? super E> c) { |
参考文献
https://zongwenlong.github.io/2017/01/06/Java-SourceCode-Sort/https://blog.csdn.net/lyj1597374034/article/details/106720629