Java ArrayList和LinkedList的区别是什么?

ArrayList 和 LinkedList 完整区别对比

一、底层数据结构

  1. ArrayList

    底层:动态 Object 数组 ,内存连续存储。

    初始化空数组,第一次 add 扩容到 10,每次扩容 1.5 倍,依靠 System.arraycopy 复制数组。

  2. LinkedList

    底层:双向链表 ,每个节点 Node 包含:数据、前驱指针、后继指针。

    内存不连续,没有扩容概念,新增元素只新建节点修改指针。

二、核心性能差异(时间复杂度)

操作 ArrayList LinkedList
get(index) 随机查询 O(1) 极快 O(n),需要从头/尾遍历到下标
尾部 add() O(1)(不扩容) O(1)
头部/中间 add(index) O(n),大量数组移位复制 O(n),仅修改指针,无数据拷贝
头部/中间 remove(index) O(n),数组前移覆盖 O(n),仅修改前后节点指针
按对象 remove(Object) O(n) O(n)

三、内存占用

  • ArrayList:只存元素,内存开销小;数组预留容量会有闲置空间。
  • LinkedList:每个元素包装成 Node,额外存两个指针(prev、next),内存开销更大。

四、线程安全

两者都非线程安全 ,多线程并发读写会抛出 ConcurrentModificationException

同步方案统一:Collections.synchronizedList()

五、支持的特性

  1. ArrayList

    实现 RandomAccess 标记接口,支持快速随机访问,推荐普通for循环遍历。

  2. LinkedList

    没有 RandomAccess;额外实现 Deque 双端队列接口,可当栈、队列、双端队列使用:

java 复制代码
list.push(); // 栈头插入
list.pop();  // 栈头弹出
list.pollFirst();
list.addLast();

六、遍历效率

  1. ArrayList:普通 for 循环 > foreach > 迭代器
  2. LinkedList:禁止用普通for循环 get(i),每次get都会从头遍历,性能极差;只能用 foreach / Iterator。

七、适用场景

优先使用 ArrayList

  1. 大量查询、根据下标取值;
  2. 只在尾部增删元素;
  3. 数据量不大、遍历频繁;
  4. 需要随机访问。

优先使用 LinkedList

  1. 频繁在头部、中间插入/删除;
  2. 需要充当队列、栈、双端队列;
  3. 极少通过下标查询元素。

八、常见误区

  1. 误区:LinkedList 所有增删都比 ArrayList 快
    纠正:尾部新增两者差不多;中间插入大量数据时,ArrayList 需要数组复制,LinkedList 更快;单纯尾部添加差距很小。
  2. 误区:LinkedList 不需要扩容所以一定更快
    纠正:链表节点创建、指针操作有开销,大量查询场景远慢于数组。
  3. 误区:for循环遍历LinkedList没问题
    纠正:for(int i=0;i<size;i++) list.get(i) 时间复杂度 O(n²),数据多会非常卡顿。

九、简短总结背诵版(面试常用)

  1. 底层:ArrayList 动态数组;LinkedList 双向链表。
  2. 查询:ArrayList 随机访问快;LinkedList 慢。
  3. 中间/头部增删:ArrayList 移位复制慢;LinkedList 修改指针快。
  4. 内存:ArrayList 紧凑;LinkedList 节点指针额外开销。
  5. 接口:LinkedList 实现Deque,可做栈队列;ArrayList 支持随机访问。
  6. 使用场景:查询多用ArrayList;频繁头尾插入删除、做队列用LinkedList。