Java源码中的排序算法(一)--Arrays.sort()

在面试准备排序算法相关内容时,死记硬背往往只能生搬硬套,难以给面试官留下深刻印象。若围绕 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,做一些简单的 究,并总结经验,如有遗漏或不合理的地方,欢迎你提出问题,让我们一起探索

相关推荐
树若逝花若殇几秒前
基于Spring AOP优雅记录请求响应日志
java·spring boot
Yyyyyy~27 分钟前
【java】数组的定义和使用
java
麻辣布丁28 分钟前
java集合篇面试模拟
java·开发语言·面试
niucloud-admin30 分钟前
JAVA V6 多商户商城 开发文档——插件开发规范
java·开发语言
嘟哩DuliDuli43 分钟前
创作 Agent 的任务状态该如何保存
android·java·javascript
wdfk_prog1 小时前
lely-canopen-rtt:为什么推荐在 RT-Thread 上使用 Lely CANopen 构建主站
java·运维·缓存·docker·容器
Token掘金室1 小时前
JSON模式结构化输出报错
java·服务器·json
秋饼1 小时前
Spring AI 2.0 接入 DeepSeek V4.1 Flash 生产级实战
java·ai·技术分享·后端开发
IT枫斗者枫哥1 小时前
Java 分批导出仍然 OOM?用 32 MiB 堆复现三种 CSV 写法
java
shehuiyuelaiyuehao1 小时前
算法43,外观数列,模拟算法+双指针
java·算法