背八股的时候,数据结构和排序总是绕不开的坎。很多人把教材里的图、PPT上的伪代码都背得滚瓜烂熟,一上机连一个冒泡排序都写不利索,更别提“HashMap怎么按value排序”“几千万条数据里取Top100”这种面试必考、工作必用的场景了。这篇文章不做理论推导,只聊Java里怎么把数据结构选型和排序落到代码里:从Collections.sort的底层逻辑到不排序怎么取最小的十个元素,从点击表头排序的通用方案到线上OutOfMemoryError的排查记录。如果你正在准备Java面试,或者在工作中被排序问题折磨过,这篇应该能帮上忙。
1. 为什么Java面试总绕不开数据结构和排序
1.1 集合框架就是数据结构的第一现场
你在面试时被问到的“数据结构”,在Java里其实都活在java.util包里。ArrayList是动态数组,LinkedList是双向链表,HashMap是数组加链表加红黑树,TreeMap是红黑树,PriorityQueue是堆。面试官问数据结构,本质上是在问这些类背后的存储模型和复杂度表现。
很多人会背“ArrayList查询O(1),插入O(n);LinkedList插入O(1),查询O(n)”,但没想过这句话该怎么用。举个例子,如果你要维护一个按访问顺序排列的记录列表,频繁在头部插入新记录,LinkedList确实有优势;但如果你要做的是一次性把所有数据拉出来,然后按第几个位置去读,ArrayList的随机访问能力会让LinkedList望尘莫及。我用过一个统计模块,遍历一个几十万条的List去get(i)取值,用LinkedList跑了近十秒,换ArrayList之后秒出结果。
HashMap更是高频考点。它的底层是数组加链表,链表长度超过阈值会转成红黑树,每次扩容要重新散列。你光记住“HashMap无序”这句话不够,还得知道它遍历顺序取决于哈希值分布。正因如此,当面试题问“如何对HashMap排序”时,本质上考的是你对Entry这个结构的拆解能力,以及对Collections.sort的熟练程度。
所以我的建议是:学数据结构时,先把Java集合框架的源码当作参考实现读一遍。源码比PPT靠谱得多,因为你能看到真实的扩容逻辑、比较器的使用方式,以及各种边界条件的处理。
1.2 排序不只是冒泡快排,工程排序的底层逻辑可能和你想的不同
Java早就提供了现成的排序工具,但很多开发者用了一年Arrays.sort,也没搞明白它内部在干什么。我拆开说一下。
Arrays.sort对基本类型数组,比如int[]、long[],走的是DualPivotQuicksort,也就是双轴快速排序。它的平均时间复杂度是O(n log n),但不稳定。什么叫不稳定?就是两个值相等的元素,排序后相对位置可能互换。对基本类型来说这无所谓,因为两个int值相同就是不可区分的。
但对对象数组,比如Integer[]或者List对象,Arrays.sort走的是TimSort。TimSort是归并排序和插入排序的混合体,最显著的特点是稳定,而且对“基本有序”的数据集可以达到近乎线性的性能。Collections.sort底层调用的也是它。
这就引出一个容易踩的坑:你有一个Student对象的List,先按班级排序,再按分数排序,如果第二次排序用了不稳定的算法,同分的学生之前排好的班级顺序就乱了。稳定排序在这种情况下能保住上一次排序的结果,不稳定排序则可能让你丢掉前一轮的排序状态。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 常用排序算法在Java里的正确写法与性能实测
2.1 冒泡、选择、插入:写出来容易,优化才是差距
先拿冒泡排序开刀。教科书版本的写法一般是双重循环,内层相邻比较交换。但你知道吗,如果某一轮循环中一次交换都没有发生,说明列表已经有序,可以直接退出。
java复制public static void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
boolean swapped = false;
for (int j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
