讨论 Arrays.sort 与 Collections.sort 的性能差异时,许多开发者首先想到的是“算法不同”。事实上,从 Java 7 开始两者均采用 TimSort 算法,真正的区别在于数据处理路径——其中一种方法比另一种少了两次不必要的数据搬运。
简单来说,Arrays.sort 直接对底层数组进行操作,无需中间转换;而 Collections.sort 则需要先将 List 转为数组,排序完成后再写回。这个过程,尤其是遇到 LinkedList 等“非随机访问”实现时,会额外复制整个数据集,白白浪费 O(n) 的时间和空间。

底层调用关系决定性能落差
Collections.sort 自身不执行排序——它先将 List 转换为 Object[] 数组,再调用 Arrays.sort(Object[], Comparator),排序完成后将结果写回原 List。但对于不同的 List 实现,这一过程的代价差异巨大:
- ArrayList.sort() 内部直接调用 Arrays.sort((Object[]) elementData, 0, size, comparator),复用底层数组,减少了显式复制步骤,但仍需通过 List 接口间接提取数组,存在少量抽象层开销。
- LinkedList.sort() 则更为低效:需先 new Object[size] 复制所有元素,排序完成后逐个 set 回链表——相当于 O(n) 时间加 O(n) 空间的“双倍代价”。
- Arrays.asList(xxx).sort() 虽然返回的是 List,但其实际实现是 Arrays 的私有静态内部类,其 sort 方法仍调用 Arrays.sort。不过,前提是传入的数组必须可修改(例如 new Integer[]{...}),否则会抛出 UnsupportedOperationException。
基本类型场景:Arrays.sort 具有绝对优势
对于 int[]、double[] 等基本类型数组,只能使用 Arrays.sort。Collections.sort 不接受基本类型集合(int 不是泛型参数),如果非要使用 Integer[] 结合 Arrays.asList 再通过 Collections.sort 排序,就必须承受自动装箱、包装类数组创建以及大量临时对象带来的开销。
- 对 10 万个整数的基准测试显示:Arrays.sort(int[]) 比 Collections.sort(Arrays.asList(Integer[])) 快 1.5 至 2 倍。
- 自动装箱的开销不容忽视:频繁调用 Integer.valueOf(i) 会生成大量临时对象,增加 GC 压力。
- 基本类型排序无需 Comparable 接口或比较器,JVM 可进行更深入的优化。
对象数组 vs List:稳定性与可控性权衡
两者默认均提供稳定排序(相等元素的相对顺序保持不变),但保障机制存在细微差异:
- Arrays.sort(Object[]) 采用 TimSort(Java 7+),稳定且对部分有序数据尤其高效。
- Collections.sort(List) 同样调用 TimSort,但由于 List 接口的抽象层级,对于非随机访问实现(如 LinkedList)无法跳过遍历,排序前的“转数组”步骤已破坏数据局部性。
- 如果需要极致可控性——例如指定并行排序、自定义阈值、避免 GC 干扰——直接使用 Arrays.sort 更为透明;如果业务逻辑本身就是基于 List(如 Spring Data 返回的 Page
),那么使用 Collections.sort 在语义上更清晰,无需为性能刻意转换数组。
真实建议:按数据源头选,不为“看起来高级”绕路
不要为了统一 API 而将数组转为 List 再使用 Collections.sort,也不要把 List 强制转换为数组后套用 Arrays.sort——类型不匹配会导致编译错误或运行时异常。
- 手头是 int[]、String[]、MyObj[]?→ 用 Arrays.sort
- 手头是 ArrayList、CopyOnWriteArrayList、Vector?→ 用 Collections.sort
- 手头是 LinkedList 或自定义 List 实现?→ 先想想是不是真需要它;如果必须排序,评估一下能不能先转 ArrayList 再排
- 性能敏感的批量处理(如日志聚合、实时计算)?→ 尽量保持数组形态,别给数据穿多余的外套
