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,做一些简单的 究,并总结经验,如有遗漏或不合理的地方,欢迎你提出问题,让我们一起探索

相关推荐
阿里巴巴P8高级架构师2 小时前
从0到1:用 Spring Boot 4 + Java 21 打造一个智能AI面试官平台
java·后端
stevenzqzq2 小时前
trace和Get thread dump的区别
java·android studio·断点
桦说编程2 小时前
并发编程踩坑实录:这些原则,帮你少走80%的弯路
java·后端·性能优化
程序猿零零漆2 小时前
Spring之旅 - 记录学习 Spring 框架的过程和经验(十三)SpringMVC快速入门、请求处理
java·学习·spring
BHXDML2 小时前
JVM 深度理解 —— 程序的底层运行逻辑
java·开发语言·jvm
tkevinjd2 小时前
net1(Java中的网络编程、TCP的三次握手与四次挥手)
java
码头整点薯条2 小时前
基于Java实现的简易规则引擎(日常开发难点记录)
java·后端
J2虾虾2 小时前
Java使用的可以使用的脚本执行引擎
java·开发语言·脚本执行
老马识途2.02 小时前
java处理接口返回的json数据步骤 包括重试处理,异常抛出,日志打印,注意事项
java·开发语言