Skip to content

实现原理

一、ArrayList底层核心实现

ArrayList 底层基于动态数组实现,核心数据结构为 Object[] elementData,通过数组的连续内存存储元素,并支持自动扩容机制。

  • 初始状态:无参构造创建时,elementData 指向共享空数组,初始容量为 0
  • 延迟初始化:第一次调用 add() 方法时,才会将数组扩容到默认容量 10
  • 扩容规则:每次扩容为原容量的 1.5 倍,通过 Arrays.copyOf() 复制旧数据到新数组

二、ArrayList添加数据的完整流程

  1. 容量校验:调用 ensureCapacityInternal(size + 1),确保数组容量足够容纳新元素
  2. 容量计算:通过 calculateCapacity() 方法,判断是否为第一次添加元素(无参构造场景),并计算所需最小容量
  3. 扩容触发:若所需容量超过当前数组长度,调用 grow() 方法执行扩容
  4. 数据存储:将新元素存入 elementData[size] 位置,size 自增 1
  5. 返回结果:返回 true 表示添加成功

三、扩容机制详解

1. 扩容触发条件

size + 1 > elementData.length 时,触发扩容操作,确保后续添加元素时有足够的空间。

2. 扩容核心逻辑

java
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    // 新容量 = 原容量 + 原容量/2(即原容量的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);
}

3. 扩容过程

  • 创建一个新的数组,长度为原数组的1.5倍
  • 通过 Arrays.copyOf() 将原数组中的元素复制到新数组中
  • elementData 指向新数组,完成扩容

四、面试高频问题解析

  1. 问:ArrayList底层的实现原理是什么?答: ArrayList底层基于动态数组实现,使用 elementData 数组存储元素。无参构造初始容量为0,第一次添加元素时扩容到10;后续扩容为原容量的1.5倍,每次扩容通过 Arrays.copyOf() 复制数据。添加元素时先校验容量,不足则扩容,再将元素存入数组并更新 size

  2. 问:ArrayList list = new ArrayList(10) 中的 list 扩容几次?答: 该语句仅初始化了一个容量为10的数组,未发生任何扩容。因为构造函数直接创建了长度为10的数组,只有当元素数量超过10时(如添加第11个元素),才会触发第一次扩容,新容量变为15。

  3. 问:ArrayList的扩容为什么是1.5倍?答: 选择1.5倍扩容是时间与空间的折中:

    • 若扩容倍数过小(如1.1倍),会导致频繁扩容,数组拷贝开销大
    • 若扩容倍数过大(如2倍),会造成较多的空间浪费
    • 1.5倍扩容既能减少扩容次数,又能避免过度浪费空间,同时配合右移运算(oldCapacity >> 1)实现高效计算
  4. 问:ArrayList的初始容量为什么不直接设为10?答: 为了优化内存占用,采用了延迟初始化策略:

    • 无参构造创建时,不直接创建长度为10的数组,而是使用共享空数组
    • 仅当第一次添加元素时,才会初始化容量为10
    • 避免了创建空集合时就占用不必要的内存空间

Powered by VitePress 1.6.4 | 持续更新中