arrays.sort对基本类型用双轴快排、对象数组用timsort,因基本类型无需稳定性而对象排序默认要求稳定;双轴快排更快且原地排序,timsort稳定但需额外空间;自定义对象无法直接切换算法,需权衡稳定性与性能。

Arrays.sort为什么对基本类型用双轴快排,而对象数组用归并排序
因为稳定性要求不同:基本类型排序不需要保持相等元素的相对位置,对象排序默认要求稳定(比如按姓名排序时,同名者原有顺序不能乱)。Arrays.sort在JDK 7+中做了明确拆分:int[]等用的是双轴快排(DualPivotQuicksort),而Object[]走的是Timsort(本质是优化版归并)。
常见错误现象:有人把自定义类数组传给Arrays.sort却没实现Comparable,直接抛ClassCastException;或者误以为所有重载都用同一套逻辑,结果性能表现和预期不符。
- 双轴快排对基本类型更快,平均时间复杂度仍是O(n log n),但常数更小,且原地排序不占额外空间
- 对象数组不用快排,是因为快排不稳定——哪怕加了随机化pivot,也无法保证相等对象的原始顺序
- JDK 8开始,
Arrays.sort(T[], Comparator)也用Timsort,不是快排,这点容易被文档误导
怎么让自定义对象数组也用双轴快排(或至少避免Timsort开销)
不能直接“切换”底层算法——Arrays.sort的实现是硬编码绑定类型的。但你可以绕过它,自己写快排逻辑,或改用其他结构。
使用场景:你确定数据量大、重复值多、且**完全不需要稳定性**(比如纯数值计算中间结果),又想压榨一点性能。
立即学习“Java免费学习笔记(深入)”;
- 别试图反射替换
DualPivotQuicksort——它对Object[]的入口方法是私有的,且做了类型检查 - 如果对象字段可转为基本类型(比如只比一个
int score),优先用IntStream提取后排序索引,再映射回原数组 - 用
Arrays.parallelSort?注意它对对象数组仍用Timsort,只是分段并行;对基本类型才真正并行双轴快排
双轴快排的两个pivot怎么选,以及什么情况下会退化
DualPivotQuicksort选两个pivot(lowPivot highPivot。这比单pivot减少比较次数,尤其对重复值多的数组更友好。
但退化风险依然存在:
- 极端有序数组(升序/降序)下,如果pivot选得不好,可能退化成O(n²);不过JDK实现里用了采样策略(取5个点选中间3个再定pivot),大幅缓解该问题
- 大量重复值时,若pivot恰好等于高频值,中间段会极大,递归深度变深;此时它会自动切到插入排序或堆排序兜底(
if (length ) - 阈值参数实际可调,但藏在
DualPivotQuicksort类里,不可配置;强行改需重编译rt.jar,不现实
Arrays.sort(int[]) 出现越界或死循环?先查这几个地方
这不是DualPivotQuicksort本身的bug,而是调用侧踩坑最频繁的几处。
常见错误现象:排序后数组部分元素消失、出现ArrayIndexOutOfBoundsException、甚至无限递归(极少,但JDK 7早期版本有相关issue)。
- 传入
null:直接NPE,不是越界——检查是否误把未初始化数组传进去 - 用错重载:比如把
Integer[](对象数组)当成int[]传给Arrays.sort(int[]),编译不过;但若通过泛型擦除+反射绕过,运行时会出诡异行为 - 并发修改:在排序过程中另一个线程正在写这个数组——
Arrays.sort不是线程安全的,不会加锁 - 数组长度为Integer.MAX_VALUE时,某些老JDK版本在计算中点时溢出(
(low + high) / 2),但JDK 8+已修复为low + (high - low) / 2
双轴快排的细节藏得深,但真正影响你日常写的,往往不是算法本身,而是类型匹配、null处理、和并发边界——这些地方不报错,但一出问题就难定位。










