
面试考排序算法是一回事,实际项目里用哪种是另一回事。很多人背完冒泡快排就完事了,但真到写代码的时候,Arrays.sort() 和 Collections.sort() 就够用了——那为什么还要学排序?因为理解了底层,才能在特殊场景做出正确选型。
一、先搞清楚:JDK 里用的是什么
打开 JDK 源码你会发现,Java 的排序不是一种算法包打天下:
| API | 底层实现 | 适用场景 |
|---|---|---|
Arrays.sort(int[]) |
双轴快速排序 | 基本类型数组 |
Arrays.sort(Object[]) |
归并排序(TimSort) | 对象数组 |
Collections.sort() |
TimSort | List 集合 |
为什么对象用归并不用快排? 因为对象排序需要稳定性——两个相等的对象排完序后,它们的相对顺序不能变。快排是不稳定的,归并是稳定的,所以 JDK 选了归并(实际上是优化版的 TimSort)。
这个细节面试很爱问:为什么 Arrays.sort() 对基本类型和对象用不同算法?答案就是稳定性。
二、什么场景需要自己写排序
大部分业务场景,调 JDK 的 sort 就够了。但以下几种情况,你需要理解排序原理甚至自己实现:
1. 数据量极小:只有几个元素,快排的递归开销反而大,插入排序更合适。这也是为什么 JDK 在 TimSort 里,小数组会切换到插入排序。
2. 数据近乎有序:插入排序在近乎有序的数据上接近 O(n),比快排快得多。如果业务场景里数据本来就大体有序(比如日志按时间排,只需要微调),插入排序是更好的选择。
3. 需要稳定排序:多字段排序时,先按 A 字段排,再按 B 字段排,如果不稳定,第二次排序会打乱第一次的结果。这时候必须用稳定排序(归并、插入、冒泡)。
4. 空间受限:嵌入式或内存极紧张的环境,归并的 O(n) 额外空间可能接受不了,这时候堆排序(O(1) 空间)更合适。
5. 数据分布均匀且范围已知:比如排序学生成绩(0-100),桶排序 O(n) 就搞定了,没必要 O(n log n)。
三、常见误区
误区1:快排一定比冒泡快。
错。n=10 的时候,冒泡可能比快排还快——快排有递归开销和分区操作,数据量小的时候常数因子更大。
误区2:归并一定比快排慢。
错。归并是稳定的 O(n log n),快排平均也是 O(n log n),但快排最坏是 O(n²)。对性能要求严格的场景,归并的时间更可控。
误区3:桶排序总是最快。
错。桶排序只在数据分布均匀、范围已知时快。如果数据全堆在一个桶里,就退化成 O(n²) 了。
误区4:排序算法背下来就行。
不对。面试考的是你能不能根据场景选对算法,以及说出为什么。
四、选型决策树
遇到排序需求时,可以按这个思路判断:
- 数据量多大? 小于 50 个 → 插入排序;大于 → 继续
- 需要稳定性吗? 需要 → 归并/TimSort;不需要 → 继续
- 数据范围已知且分布均匀? 是 → 桶排序/计数排序;不是 → 继续
- 空间敏感吗? 敏感 → 堆排序;不敏感 → 快排或归并
大部分时候,你直接调 JDK 的 sort 就对了。但知道底层为什么这么选,面试和排查问题时都用得上。
五、写在最后
排序算法不是为了让你手写,而是帮你建立"算法选型"的思维方式——面对一个问题,先分析数据特征,再选合适的算法,而不是上来就套最熟的那个。
更多排序算法的可视化演示和细节可以看原文——查看八种经典排序算法完整总结。
你在项目里手写过排序算法吗?什么场景?评论区交流。