在面试准备排序算法相关内容时,死记硬背往往只能生搬硬套,难以给面试官留下深刻印象。若围绕 Java 中常用的 Arrays.sort() 展开讲解,能让你的知识储备更具实用性和深度,展现出对实际开发的理解。以下将从 Arrays 类的排序方法分类、源码实现,以及核心排序算法/类的能力详解三方面展开。(所有源码基于Java21)
一、Arrays 类与排序方法分类
Arrays 是 Java 提供的用于操作数组的工具类,其排序方法可根据处理的数据类型(基本数据类型/引用数据类型)和排序方式(普通排序/并发排序)分为六大类,核心逻辑围绕"适配不同数据场景选择最优排序算法"展开,具体分类可参考如下逻辑框架:
- 基本数据类型排序
- 普通排序:
sort(int[] a)、sort(long[] a)等 - 并发排序:
parallelSort(int[] a)、parallelSort(long[] a)等
- 普通排序:
- 引用数据类型排序(元素父类为 Object)
- 普通排序:
sort(Object[] a)、sort(T[] a, Comparator<? super T> c)等 - 并发排序:
parallelSort(T[] a)(T 实现 Comparable 接口)
- 普通排序:
二、Arrays.sort() 源码核心实现
通过阅读 JDK 源码(以 JDK 21 为例),可发现 Arrays 的排序方法会根据数据类型选择不同的底层算法,具体实现如下:
1. 基本数据类型
无论是普通排序(sort(int[] a))还是并发排序(parallelSort(int[] a)),基本数据类型数组的排序均依赖 DualPivotQuicksort 类。
java
/**
* 对 int 类型数组进行升序排序
* @implNote 底层使用双轴快速排序(Dual-Pivot Quicksort),由 Vladimir Yaroslavskiy、Jon Bentley 和 Joshua Bloch 设计
* 该算法在所有数据集上均能保证 O(n log n) 的时间复杂度,且通常比传统单轴快速排序更快
*/
public static void sort(int[] a) {
DualPivotQuicksort.sort(a, 0, 0, a.length);
}
/**
* 对 int 类型数组进行并行升序排序(JDK 8+ 新增)
* @implNote 底层仍基于 DualPivotQuicksort,结合 Fork/Join 框架实现并行处理,提升大规模数组排序效率
*/
public static void parallelSort(int[] a) {
DualPivotQuicksort.sort(a, ForkJoinPool.getCommonPoolParallelism(), 0, a.length);
}
2. 引用数据类型
引用数据类型(如 Object[]、自定义类数组)的排序逻辑相对复杂,会根据是否开启(LegacyMergeSort)选择不同算法,核心目标是保证稳定性(相等元素不改变原有顺序)。
2.1 普通排序:sort(Object[] a) 与 sort(T[] a, Comparator<? super T> c)
根据是否开启"遗留归并排序"(LegacyMergeSort),引用数据类型数组的普通排序会选择不同算法:
- 若开启
LegacyMergeSort,则使用遗留归并排序(legacyMergeSort),保证稳定性; - 若未开启
LegacyMergeSort,则使用基于 TimSort 优化的排序(ComparableTimSort),也能保证稳定性。
java
/**
* 按元素自然排序(需实现 Comparable 接口)对 Object 数组升序排序,保证稳定性
* @implNote 若用户通过系统属性开启 legacyMergeSort,则使用遗留归并排序;否则使用 ComparableTimSort
*/
public static void sort(Object[] a) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a); // 遗留归并排序
else
ComparableTimSort.sort(a, 0, a.length, null, 0, 0); // 基于 TimSort 优化的排序
}
/**
* 按自定义 Comparator 对数组升序排序,保证稳定性
* @implNote 逻辑与 sort(Object[] a) 一致,无 Comparator 时复用自然排序,有 Comparator 时用 TimSort
*/
public static <T> void sort(T[] a, Comparator<? super T> c) {
if (c == null) {
sort(a); // 无 Comparator 时按自然排序
} else {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, c); // 带 Comparator 的遗留归并排序
else
TimSort.sort(a, 0, a.length, c, null, 0, 0); // 带 Comparator 的 TimSort
}
}
2.2 并发排序:parallelSort(T[] a)
JDK 8 新增的并行排序方法,针对引用数据类型数组,底层通过"分治+并行合并"实现,子数组排序依赖 TimSort,最终通过 ArraysParallelSortHelpers.FJObject 类封装 Fork/Join 任务。
java
/**
* 对实现 Comparable 接口的引用类型数组进行并行升序排序,保证稳定性
* @implNote 算法逻辑:将数组拆分为多个子数组,子数组排序用 TimSort,最后并行合并所有有序子数组
* 若数组长度小于最小粒度(MIN_ARRAY_SORT_GRAN),则退化到普通 TimSort
*/
@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> void parallelSort(T[] a) {
int n = a.length, p, g;
if (n <= MIN_ARRAY_SORT_GRAN || (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
// 数组过短或无并行能力时,直接用 TimSort
TimSort.sort(a, 0, n, NaturalOrder.INSTANCE, null, 0, 0);
else
// 封装 Fork/Join 任务,并行处理子数组
new ArraysParallelSortHelpers.FJObject.Sorter<>
(null, a,
(T[])Array.newInstance(a.getClass().getComponentType(), n),
0, n, 0,
((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ? MIN_ARRAY_SORT_GRAN : g,
NaturalOrder.INSTANCE).invoke();
}
3. 用一张图总结陈词
三、核心排序算法/类能力介绍
Arrays.sort() 底层依赖的 5 个核心算法/类,先做大致了解:
- DualPivotQuicksort(双轴快速排序) :专为基本数据类型设计的高效排序算法,是 JDK 6+ 中基本数据类型排序的默认实现,替代了早期的单轴快速排序。
- legacyMergeSort(归并排序) :JDK 1.2 引入的传统归并排序实现,在 JDK 6 后被 TimSort 替代,但通过系统属性
java.util.Arrays.useLegacyMergeSort=true可手动开启,用于兼容依赖其稳定性的旧代码。 - TimSort(蒂姆排序):由 Tim Peters 为 Python 设计,JDK 6 引入并作为引用数据类型排序的默认算法(除 legacyMergeSort 开启场景),是"归并排序+插入排序"的混合优化算法。
- ComparableTimSort(基于 Comparable 的 TimSort) :TimSort 的变体,专门用于按元素自然排序 (即元素实现
Comparable接口)的引用数据类型数组,是sort(Object[] a)方法的默认实现(未开启 legacyMergeSort 时)。 - ArraysParallelSortHelpers.FJObject :JDK 8 为支持引用数据类型并行排序 (
parallelSort(T[] a))而设计的工具类,封装了 Fork/Join 框架的并行任务逻辑,是"并行排序"的核心调度组件。
这一篇我们先点到位置,通过源码大致知道了
Arrays.sort()的不寻常,接下来连载详细拆解其中奥妙💡 感谢你看完这篇内容,这是我自己在工作学习中遇到的case,做一些简单的 究,并总结经验,如有遗漏或不合理的地方,欢迎你提出问题,让我们一起探索。
