267. Java 集合 - Java 开发必看:ArrayList 与 LinkedList 的全方位对比及选择建议

267. Java 集合 - Java 开发必看:ArrayList 与 LinkedList 的全方位对比及选择建议

✅ 总体结论

在绝大多数经典的 List 操作中,ArrayList 是更好的选择 。它在性能和内存使用方面通常优于 LinkedList,尤其当你只是需要一个常规的顺序集合时。


🔍 为什么 ArrayList 更优?

1️⃣ 遍历性能

虽然 LinkedList 的插入和删除操作理论上时间复杂度为 O(1) ,但实际性能却被"指针追踪(Pointer Chasing)"拖慢了

  • LinkedList 是基于双向链表的,每个元素都包裹在 Node 对象中,包含 prevnext 指针。
  • 每次访问特定索引的元素时,需要从头或尾开始遍历节点,非常慢。

📌 示例对比:

java 复制代码
List<Integer> arrayList = new ArrayList<>();
List<Integer> linkedList = new LinkedList<>();

for (int i = 0; i < 1_000_000; i++) {
    arrayList.add(i);
    linkedList.add(i);
}

// 随机读取某个元素
int x1 = arrayList.get(500_000); // O(1)
int x2 = linkedList.get(500_000); // O(n) -- 实际性能明显更慢

2️⃣ 插入性能对比

虽然 LinkedList 在中间插入时不需要移动元素,但你仍然需要遍历到目标位置

  • ArrayList 中间插入:需要调用 System.arraycopy() 移动元素,时间复杂度 O(n)
  • LinkedList 中间插入:遍历找到目标位置(O(n))+ 修改指针(O(1))。

📌 示例说明:

java 复制代码
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add(1, "Eve"); // 插入到中间,需要移动元素

🧠 结论:*虽然 ArrayList 插入成本较高,但总体上* 仍然优于 LinkedList,因为访问目标位置更快。


3️⃣ 扩容机制影响性能

ArrayList 会自动扩容,其代价是一次性分配更大的数组,并将旧元素拷贝过去。

你可以通过构造函数提前设置容量,避免频繁扩容:

📌 推荐做法:

java 复制代码
List<Integer> list = new ArrayList<>(10_000); // 提前分配,避免多次扩容

🔁 LinkedList 更适合什么场景?

如果你的数据结构本质上是**队列(Queue)*或者*栈(Stack) ,并且只涉及首尾元素的访问,LinkedList 是一个不错的选择。

java 复制代码
Deque<Integer> deque = new LinkedList<>();
deque.addFirst(1);  // 等价于 push
deque.addLast(2);   // 等价于 offer
deque.removeFirst(); // 等价于 pop

✅ 更推荐使用:ArrayDeque,性能更好、内存占用更低


🧠 内存使用分析

1️⃣ 总体内存占用对比(以存储 1_000 个元素为例):

实现方式 大致内存占用
ArrayList ~4,976 bytes
LinkedList ~24,032 bytes
  • LinkedList 每个节点有额外的对象头和两个引用字段(prevnext)。
  • ArrayList 内部是一个普通数组,节省指针和对象头开销。

2️⃣ 少量元素时的差异(比如只存一个元素)

实现 内存占用
ArrayList 默认容量(10) ~76 bytes
ArrayList trimToSize 后 ~44 bytes
LinkedList ~56 bytes
List.of(1)(不可变) ~26 bytes ✅最省内存

📌 示例说明:

java 复制代码
List<Integer> a1 = new ArrayList<>();
a1.add(1); // 默认内部数组大小为 10,占内存大

List<Integer> a2 = new ArrayList<>(List.of(1)); // 内部数组大小为 1,占用小

List<Integer> a3 = List.of(1); // 不可变列表,最省内存

3️⃣ 内存不会自动释放?

ArrayList 的内部数组在删除元素后不会自动缩小。

java 复制代码
List<Integer> list = new ArrayList<>(1000);
list.add(1);
list.clear(); // 清空,但内存仍然保留

((ArrayList<Integer>) list).trimToSize(); // 手动释放未用空间

📊 一张图表:不同情况内存占用变化(单位:字节)

元素数量 ArrayList(2) LinkedList 默认ArrayList TrimmedArrayList
0 40 32 40 40
1 48 56 80 48
2 48 80 80 48
5 64 152 80 64
10 96 272 80 80

ArrayList(2) 是一种灵活的做法,在小集合场景中效果很好,直到元素数超过 9。


✅ 总结建议

场景 推荐使用 说明
快速随机访问(get(i) ArrayList O(1)
插入/删除大量元素(尤其中间位置) ⚠️ ArrayList 虽然 O(n),仍然好过 LinkedList
队列/栈场景(只用首尾) ArrayDeque LinkedList 更高效
极度节省内存 + 不可变数据 List.of(...) 最节省内存
频繁清空并回收内存 ArrayList.trimToSize() 手动释放冗余空间
小集合、内存受限 ArrayList<>(2) 小而精的结构,性能内存兼顾
相关推荐
jingqingdai33 分钟前
别用正则格式化 HTML!我用 DOM 遍历实现零风险本地格式化,老项目重构效率直接拉满
前端·重构·html
绿草在线3 分钟前
SpringBoot项目实战:从零搭建高效开发环境
java·spring boot·后端
木斯佳6 分钟前
前端八股文面经大全:腾讯前端实习二、三OC面(2026-04-27)·面经深度解析
前端·状态模式
longxibo10 分钟前
【第1章 环境搭建与项目结构解析】
java·后端·流程图
Python私教18 分钟前
如意Agent日志系统重构:从 print() 大海捞针到结构化可观测性栈
java·前端·重构
程序员老邢35 分钟前
【产品底稿 11】架构规整收官:从混乱到清晰,工程结构、表命名、模块分层一次性定型
后端·架构·springboot·产品底稿·架构规整·模块分层·数据库规范
We་ct37 分钟前
LeetCode 97. 交错字符串:动态规划详解
前端·算法·leetcode·typescript·动态规划
Chengbei111 小时前
轻量化 Web 安全日志分析神器 星川智盾日志威胁检测、地理溯源、MITRE ATT&CK 映射,支持 Windows/macOS/Linux
前端·人工智能·安全·web安全·macos·系统安全·安全架构
风流 少年1 小时前
Python Web框架:FastAPI
前端·python·fastapi