Skip to content

源码分析

一、ArrayList核心成员变量

java
// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;

// 空实例共享的空数组(无参构造使用)
private static final Object[] EMPTY_ELEMENTDATA = {};

// 默认大小空实例共享的空数组(无参构造延迟初始化使用)
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

// 存储元素的数组缓冲区,ArrayList的容量就是该数组的长度
transient Object[] elementData;

// 集合中实际元素的数量
private int size;

二、ArrayList构造方法

1. 无参构造

java
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
  • 初始化时不直接创建长度为10的数组,而是使用共享空数组
  • 延迟初始化:在第一次调用add()方法时,才会扩容到默认容量10

2. 带初始容量构造

java
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);
    }
}
  • 指定初始容量,避免频繁扩容
  • 初始容量为0时,使用共享空数组

3. 集合构造

java
public ArrayList(Collection<? extends E> c) {
    Object[] a = c.toArray();
    if ((size = a.length) != 0) {
        if (c.getClass() == ArrayList.class) {
            elementData = a;
        } else {
            elementData = Arrays.copyOf(a, size, Object[].class);
        }
    } else {
        elementData = EMPTY_ELEMENTDATA;
    }
}
  • 将传入的集合转换为数组,赋值给elementData
  • 若集合为空,使用共享空数组

三、ArrayList添加与扩容流程

1. add()方法核心流程

java
public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}
  • ensureCapacityInternal(size + 1):确保数组容量足够
  • elementData[size++] = e:将元素存入数组,size自增

2. 确保内部容量

java
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
  • 第一次添加元素时,elementDataDEFAULTCAPACITY_EMPTY_ELEMENTDATA,计算容量为max(10, 1)=10
  • 后续添加时,直接返回minCapacity

3. 确保显式容量

java
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
  • minCapacity > elementData.length,说明容量不足,调用grow()方法扩容

4. grow()扩容方法

java
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);
}
  • 扩容规则:新容量 = 原容量 + 原容量/2(即原容量的1.5倍)
  • 若扩容后的容量仍小于minCapacity,则使用minCapacity作为新容量
  • 通过Arrays.copyOf()创建新数组,复制旧数组数据

四、关键场景分析

1. 第一次添加元素

  • 初始状态:elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA(空数组)
  • 调用add(1)时,calculateCapacity返回10,ensureExplicitCapacity触发扩容
  • grow()方法将数组扩容到10,元素存入数组,size变为1

2. 第2-10次添加元素

  • 数组容量为10,minCapacity为2-10,均不超过数组长度,无需扩容
  • 直接将元素存入数组,size自增

3. 第11次添加元素

  • minCapacity = 11,大于数组长度10,触发扩容
  • 新容量 = 10 + 10/2 = 15,数组扩容到15,元素存入数组,size变为11

五、面试相关问题

  1. 问:ArrayList的底层实现原理是什么?答: ArrayList基于动态数组实现,通过elementData数组存储元素,初始时使用共享空数组延迟初始化。添加元素时,若数组容量不足则触发扩容,每次扩容为原容量的1.5倍,并通过Arrays.copyOf()复制旧数据到新数组。

  2. 问:ArrayList list = new ArrayList(10) 中的list扩容几次?答: 不会触发自动扩容。因为指定初始容量为10,elementData直接创建长度为10的数组,添加前10个元素时无需扩容;当添加第11个元素时,才会触发第一次扩容,容量变为15。

  3. 问:ArrayList和LinkedList的区别是什么?答:

    • 底层结构:ArrayList基于动态数组,LinkedList基于双向链表;
    • 访问效率:ArrayList随机访问效率高(O(1)),LinkedList需遍历查找(O(n));
    • 增删效率:ArrayList在中间增删时需移动元素(O(n)),LinkedList在已知节点前后增删效率高(O(1));
    • 内存占用:ArrayList扩容会浪费部分空间,LinkedList每个节点需额外存储前后指针,内存开销更大。
  4. 问:ArrayList的扩容机制是什么?答: ArrayList默认初始容量为10(无参构造延迟初始化),当元素数量超过数组长度时触发扩容,新容量为原容量的1.5倍。扩容通过Arrays.copyOf()创建新数组并复制旧数据,因此扩容操作开销较大,建议预估数据量并指定初始容量,减少扩容次数。

Powered by VitePress 1.6.4 | 持续更新中