Java ArrayList && LinkedList 详解【默认参数、初始化过程、扩容过程、对比分析】

本文将从源码角度来详细对比 ArrayListLinkedList 的实现。下面会结合 JDK8/11 源码来拆解:


ArrayList 源码详解

1. 默认参数

private static final int DEFAULT_CAPACITY = 10;   // 默认初始容量
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 默认空数组
transient Object[] elementData; // 存储数据的数组
private int size; // 实际元素个数
  • 初始时,elementData 并不会直接分配长度为 10 的数组,而是用 空数组
  • 真正分配容量在第一次 add() 时发生。

2. 初始化过程

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; // 空数组
}

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];   // 用户指定容量
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;             // 空数组
    } else {
        throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
    }
}

3. 添加元素与扩容

public boolean add(E e) {
    ensureCapacityInternal(size + 1); // 确保容量足够
    elementData[size++] = e;
    return true;
}
扩容逻辑
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容1.5倍
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    elementData = Arrays.copyOf(elementData, newCapacity);
}
  • 扩容比例:旧容量的 1.5 倍。
  • 如果 1.5 倍仍不足以容纳新元素,则直接使用 minCapacity

4. 使用特点

  • 随机访问快(O(1),基于数组索引)。
  • 插入/删除慢(O(n),需要移动元素)。
  • 适合 查多改少 的场景。

LinkedList 源码详解

1. 数据结构定义

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
  • 双向链表结构。
  • first 指向头结点,last 指向尾结点。

2. 初始化过程

public LinkedList() {}
  • 构造函数几乎没有逻辑,不需要预分配空间。
  • 容量自适应,随用随建。

3. 添加元素

public boolean add(E e) {
    linkLast(e);
    return true;
}

void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode; // 空链表时
    else
        l.next = newNode;
    size++;
}
  • 插入元素时,直接在尾部新建节点并链接。
  • 不涉及数组扩容。

4. 删除元素

E unlink(Node<E> x) {
    final E element = x.item;
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;

    if (prev == null)
        first = next;
    else
        prev.next = next;

    if (next == null)
        last = prev;
    else
        next.prev = prev;

    size--;
    return element;
}
  • 删除时只需修改指针,不会移动其他节点。
  • 删除效率 O(1),但前提是已经定位到目标节点。

5. 使用特点

  • 插入/删除快(只需修改前后指针)。
  • 随机访问慢(O(n),需要从头/尾遍历)。
  • 适合 改多查少 的场景。

对比总结

特性ArrayListLinkedList
底层结构动态数组双向链表
默认容量10(懒加载)无需容量,节点动态分配
扩容方式1.5 倍不需要扩容
插入/删除复杂度O(n)O(1)(已知节点)/O(n)(按索引查找)
随机访问复杂度O(1)O(n)
内存开销小(连续数组)大(存储节点和指针)
适用场景查多改少改多查少

关键数据结构对比

在这里插入图片描述


Mermaid 流程图

在这里插入图片描述

ArrayList 扩容机制(基于数组)

在这里插入图片描述

1. 默认初始化

  • 构造函数
// 默认构造函数,不会立刻分配空间
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
  • 只有在第一次 add() 时,才会初始化为 默认容量 10

2. 扩容触发点

size == elementData.length 时,触发扩容逻辑:

public boolean add(E e) {
    ensureCapacityInternal(size + 1); // 确保容量够用
    elementData[size++] = e;
    return true;
}

3. 扩容方法

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    // 新容量 = 旧容量的 1.5 倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);

    // 核心:数组拷贝
    elementData = Arrays.copyOf(elementData, newCapacity);
}
关键点:
  1. 扩容增量 = 原容量的 1/2,即 1.5 倍。
  2. 使用 System.arraycopy / Arrays.copyOf 拷贝数组,时间复杂度 O(n)。
  3. 属于 动态数组扩容,对随机访问友好。

LinkedList 扩容机制【追加节点】(基于链表)

在这里插入图片描述

1. 数据结构

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;
    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}
  • 底层是 双向链表,不需要预分配数组。

2. 添加元素(无需扩容)

public boolean add(E e) {
    linkLast(e); // 总是添加到链表尾部
    return true;
}

void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
}

3. 特性

  1. 没有扩容过程,因为链表节点是动态分配的。

  2. 新增元素时,仅仅创建一个 Node 并挂到链表尾部。

  3. 时间复杂度:

    • 插入/删除:O(1)(前提是已定位到节点)
    • 随机访问:O(n)(需要遍历链表)

总结对比

特性ArrayListLinkedList
底层结构动态数组双向链表
默认容量10无容量限制
扩容机制1.5 倍扩容 + 数组拷贝不需要扩容,节点动态分配
插入删除性能O(n)(可能触发扩容+拷贝)O(1)(若已定位节点)
随机访问性能O(1)O(n)
内存使用紧凑,数组结构额外存储 prev/next 指针,开销大

在这里插入图片描述


也就是说:

  • ArrayList 通过 扩容(grow + copy) 来保证容量。
  • LinkedList 没有扩容逻辑,直接通过链表节点动态分配内存。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

.Shu.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值