Skip to content

ArrayList vs LinkedList:一张表说清楚

核心区别

ArrayList = 数组,连续内存
  → 随机访问快(O(1)),中间增删慢(O(n))

LinkedList = 双向链表,分散内存
  → 随机访问慢(O(n)),头尾操作快(O(1))

性能对比表

操作ArrayListLinkedList胜者
随机访问 get(i)O(1)O(n)ArrayList
尾部添加 add(e)O(1) 均摊O(1)平手
头部插入 addFirst()O(n)O(1)LinkedList
中间插入 add(i, e)O(n)O(n)平手
按索引删除 remove(i)O(n)O(n)平手
迭代器删除 it.remove()O(n)O(1)LinkedList
遍历(迭代器/forEach)更快LinkedList
内存占用ArrayList
CPU 缓存友好不友好ArrayList

图解:内存结构差异

ArrayList(连续内存):
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ A │ B │ C │ D │ E │ F │ G │ H │ ...
└───┴───┴───┴───┴───┴───┴───┴───┘
  │ ← CPU 一次读一块,缓存命中率高

LinkedList(分散内存):
┌────┐    ┌────┐    ┌────┐    ┌────┐
│ A  │───▶│ B  │───▶│ C  │───▶│ D  │───▶ ...
└────┘    └────┘    └────┘    └────┘
  ↑         ↑         ↑
每次访问都要去内存里找下一个节点

实战选择:5 个场景

场景 1:按索引随机访问

java
// ✅ ArrayList
List<Record> records = new ArrayList<>(db.queryAll());
for (int i = 0; i < records.size(); i++) {
    process(records.get(i)); // O(1),极快
}

// ❌ LinkedList
List<Record> linked = new LinkedList<>(db.queryAll());
for (int i = 0; i < linked.size(); i++) {
    process(linked.get(i)); // O(n),极慢
}

场景 2:只尾部追加

java
// ✅ 两者都可以,ArrayList 稍快
List<String> list = new ArrayList<>();
// 或
List<String> linked = new LinkedList<>();

for (String s : data) {
    list.add(s); // 或 linked.add(s)
}

场景 3:迭代器遍历时增删

java
// ✅ LinkedList
List<String> list = new LinkedList<>(Arrays.asList("A", "B", "C"));
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    if (it.next().equals("B")) {
        it.remove(); // O(1),极快
    }
}

// ❌ ArrayList
List<String> array = new ArrayList<>(Arrays.asList("A", "B", "C"));
for (int i = 0; i < array.size(); i++) {
    if (array.get(i).equals("B")) {
        array.remove(i); // O(n),慢
        i--; // 还要调整索引
    }
}

场景 4:双端队列操作

java
// ✅ ArrayDeque(最快)
Deque<Integer> deque = new ArrayDeque<>();
deque.addFirst(1);
deque.addLast(2);
deque.pollFirst();

// ✅ LinkedList
Deque<Integer> linked = new LinkedList<>();
linked.addFirst(1);
linked.addLast(2);

// ❌ ArrayList(最慢)
List<Integer> array = new ArrayList<>();
array.add(0, 1); // O(n),慢

场景 5:批量数据处理后遍历

java
// ✅ ArrayList
List<Transaction> txns = db.fetchTransactions();
// 处理...
for (Transaction t : txns) { // 遍历快,CPU 缓存友好
    process(t);
}

性能实测

java
import java.util.*;

public class ListPerformanceTest {

    public static void main(String[] args) {
        int size = 100_000;

        ArrayList<Integer> array = new ArrayList<>();
        LinkedList<Integer> linked = new LinkedList<>();

        for (int i = 0; i < size; i++) {
            array.add(i);
            linked.add(i);
        }

        // 1. 随机访问
        System.out.println("随机访问 10 万次:");
        System.out.println("  ArrayList: " + measure(() -> {
            for (int i = 0; i < size; i++) array.get(i);
        }));
        System.out.println("  LinkedList: " + measure(() -> {
            for (int i = 0; i < size; i++) linked.get(i);
        }));

        // 2. 迭代器遍历
        System.out.println("迭代器遍历:");
        System.out.println("  ArrayList: " + measure(() -> {
            for (Integer i : array) { /* 消耗 */ }
        }));
        System.out.println("  LinkedList: " + measure(() -> {
            for (Integer i : linked) { /* 消耗 */ }
        }));

        // 3. 头部插入
        System.out.println("头部插入 1 万元素:");
        System.out.println("  ArrayList: " + measure(() -> {
            ArrayList<Integer> a = new ArrayList<>();
            for (int i = 0; i < 10000; i++) a.add(0, i);
        }));
        System.out.println("  LinkedList: " + measure(() -> {
            LinkedList<Integer> l = new LinkedList<>();
            for (int i = 0; i < 10000; i++) l.addFirst(i);
        }));
    }

    static String measure(Runnable r) {
        long start = System.nanoTime();
        r.run();
        return (System.nanoTime() - start) / 1_000_000 + " ms";
    }
}

典型结果:

随机访问 10 万次:
  ArrayList: 5 ms      ← 极快
  LinkedList: 2000 ms   ← 极慢

迭代器遍历:
  ArrayList: 8 ms
  LinkedList: 7 ms      ← 迭代器方式遍历反而快

头部插入 1 万元素:
  ArrayList: 1500 ms    ← 每次都要移动元素
  LinkedList: 5 ms     ← 只需改指针

避坑指南

误区真相
「LinkedList 增删比 ArrayList 快」只有迭代器增删头尾操作时,LinkedList 才是 O(1)。按索引增删都是 O(n)
「LinkedList 不需要移动元素,所以一定快」但需要遍历找到位置,O(n)。总体不一定更快
「LinkedList 内存占用更低」每个节点额外 2 个引用,内存占用更高
「LinkedList 适合做随机访问」LinkedList 不适合按索引访问,O(n)

一句话总结

ArrayList:用「索引」思考 → 随机访问多用它
LinkedList:用「迭代器」思考 → 头尾操作或迭代中增删用它
ArrayDeque:队列/栈首选 → 比 LinkedList 快 3 倍

相关链接

基于 VitePress 构建