JJava 知识库
JAVA INTERVIEW

高频面试题

Java集合基础约 3 分钟

批量处理订单时既有大量遍历又有中间插入,你会选 ArrayList 还是 LinkedList?为什么?

参考回答约 3 分钟 · 口语表达
先说结论

先说结论:从数据结构、扩容、随机访问和真实插入成本比较两种 List

01

我先给结论,再说明它在项目里解决什么问题。绝大多数业务场景优先使用 ArrayList。它的随机访问是 O(1),连续内存带来更好的 CPU 缓存局部性。LinkedList 只有在已经持有节点位置并频繁在该位置插入、删除时才可能占优。

02

核心机制我会按一次真实执行过程来讲。沿着「接收访问模式 → 选择连续数组或链节点 → 定位目标元素 → 执行读写或插入 → 扩容回收并返回」观察输入、状态与输出,这些阶段都可以从日志、指标或源码里验证。 接收访问模式 先确认主要操作是随机访问、尾部追加还是中间插入,不能只依据 Big-O 口号选择。 选择连续数组或链节点 ArrayList 用连续引用数组获得局部性,LinkedList 为每个元素额外保存前后节点指针。

03

实现细节只抓关键入口,不会整段背源码。java.util.ArrayList:grow、fastRemove 与数组移动。 java.util.LinkedList:Node、node(index) 从头尾查找。 我会先确认请求实际走到了哪条路径,再用运行数据验证,不会只看类名或配置猜测。

04

放到生产使用时,我会关注参数和验证数据。用 JMH 分别测试随机 get、中间插入、尾部追加和增强 for;数据量固定 1k/100k/1m,配合 JFR 记录 Node 分配。 固定输入和基线 先在没有故障注入的环境执行上述配置,固定数据规模、并发度、运行时版本和预热时间。以「10 万次随机 get」为主基线,记录值应满足「ArrayList 应明显更快」;同时保存 集合尺寸分布、扩容与数组复制次数,使后续变化能够回到同一时间轴比较。

05

最后补充常见误区和使用边界。代码认为链表插入快,把数据库结果装入 LinkedList 后反复 get(i) 遍历,实际退化为 O(n²)。改为增强 for 顺序遍历已经改善,但由于主要是追加与扫描,最终使用预估容量的 ArrayList 获得更低内存和更好的缓存局部性。 在 LinkedList 上按索引循环:集合尺寸分布:把索引循环改为迭代器。 方案:更适合的场景:主要收益:代价与边界。 ArrayList:随机访问、尾部追加和批量遍历:内存紧凑、CPU 缓存友好:中间移动与扩容复制。 LinkedList:频繁操作两端且直接持有迭代位置:双端增删语义直接:节点开销高、随机访问慢。 ArrayDeque:队列、栈和双端操作:连续数组、性能稳定:不支持 null,按下标访问不是主要能力。