ArrayList 与 LinkedList 的区别

ArrayList 与 LinkedList 的核心区别

在 Java 中,ArrayListLinkedList是两种常用的列表实现,它们在底层结构、性能特性和适用场景上有显著差异。以下从多个维度详细对比:

1. 底层数据结构
对比项 ArrayList LinkedList
数据结构 动态数组(Object[]) 双向链表(每个节点包含前驱和后继指针)
存储方式 连续内存空间存储元素 非连续内存,通过指针关联元素
内存占用 需预留容量(默认 10),可能浪费空间 每个节点需额外存储指针,空间开销大
2. 性能特性对比
操作类型 ArrayList LinkedList
随机访问(get/set) O(1)(直接通过索引访问) O(n)(需从头或尾遍历链表)
尾部插入(add) 平均O(1)(扩容时 O (n)) O(1)(直接插入到尾部节点)
头部插入(addFirst) O (n)(需移动所有元素) O(1)(修改头指针)
中间插入(add (index)) O (n/2)(平均移动一半元素) O (n/2)(平均需遍历到中间节点)
删除操作(remove) O (n/2)(移动元素填补空缺) O (n/2)(需先定位节点)
3. 扩容机制
  • ArrayList
    初始容量为 10,扩容时新容量为原容量的1.5 倍oldCapacity + (oldCapacity >> 1))。
    扩容需复制数组,开销较大。

  • LinkedList
    无需预分配容量,通过指针动态添加节点,无扩容开销。

4. 线程安全性
  • 两者均非线程安全
    若需线程安全,可通过以下方式:

    java

    // ArrayList线程安全包装
    List safeArrayList = Collections.synchronizedList(new ArrayList<>());
    
    // LinkedList线程安全包装
    List safeLinkedList = Collections.synchronizedList(new LinkedList<>());
    

    或使用CopyOnWriteArrayList(适用于读多写少场景)。
5. 适用场景
场景 推荐选择 原因
随机访问频繁 ArrayList O (1) 时间复杂度,数组天然支持随机访问。
插入 / 删除频繁(尤其是尾部) ArrayList 尾部插入平均 O (1),优于 LinkedList 的指针操作开销。
插入 / 删除频繁(尤其是头部或中间) LinkedList 无需移动元素,仅需修改指针。
内存敏感 ArrayList 无额外指针开销,空间利用率高(需合理控制初始容量)。
需双端队列操作 LinkedList 实现Deque接口,支持高效的头 / 尾操作(如addFirst()pollLast())。
6. 代码示例对比

java

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class ListComparison {
    public static void main(String[] args) {
        // ArrayList示例
        List arrayList = new ArrayList<>();
        arrayList.add("A");           // 尾部插入快
        arrayList.add(0, "B");        // 头部插入需移动元素,慢
        String element = arrayList.get(1);  // 随机访问快

        // LinkedList示例
        LinkedList linkedList = new LinkedList<>();
        linkedList.addFirst("C");     // 头部插入快
        linkedList.addLast("D");      // 尾部插入快
        linkedList.removeFirst();     // 头部删除快
    }
}
7. 总结对比表
特性 ArrayList LinkedList
底层结构 动态数组 双向链表
随机访问 极快(O (1)) 慢(O (n))
头部插入 / 删除 慢(O (n)) 快(O (1))
尾部插入 / 删除 快(O (1),扩容时 O (n)) 快(O (1))
中间插入 / 删除 慢(O (n)) 慢(O (n),但指针操作开销小)
内存占用 数组预分配,可能浪费空间 每个节点需额外存储两个指针
实现接口 List ListDeque
适用场景 随机访问为主,尾部增删较多 频繁头部 / 中间增删,双端队列操作
8. 面试常见问题
  1. 为什么 ArrayList 随机访问快?
    答:数组在内存中连续存储,通过索引可直接计算内存地址(偏移量)。

  2. 为什么 LinkedList 插入删除快?
    答:插入 / 删除仅需修改指针,无需移动元素(但需先定位节点,时间复杂度仍为 O (n))。

  3. ArrayList 的扩容机制是什么?
    答:初始容量 10,扩容时新容量为原容量的 1.5 倍,通过Arrays.copyOf()复制数组。

  4. 如何选择 ArrayList 和 LinkedList?
    答:根据访问和修改操作的频率选择。若随机访问多,选 ArrayList;若频繁在头部 / 中间插入删除,选 LinkedList。

总结
  • ArrayList适合随机访问频繁尾部增删较多的场景,牺牲部分插入性能换取高效的随机访问。
  • LinkedList适合频繁头部 / 中间增删需双端队列操作的场景,牺牲随机访问性能换取灵活的插入删除。
    在实际开发中,需根据具体业务场景权衡选择,避免因误用导致性能问题。

你可能感兴趣的:(Java基础,java,面试)