【Java】ArrayList核心源码解析与扩容机制详解


一、底层数据结构

// JDK 1.8 ArrayList部分源码
transient Object[] elementData; // 存储元素的数组缓冲区
private int size; // 当前元素数量

二、构造方法解析

// 无参构造(使用默认容量)
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(...);
    }
}

// 集合参数构造
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

三、关键成员变量

变量名作用
DEFAULT_CAPACITY默认初始容量10
MAX_ARRAY_SIZE最大数组长度Integer.MAX_VALUE - 8
modCount结构性修改计数器(用于快速失败机制,迭代时检测并发修改)

三、add方法执行流程

public boolean add(E e) {
    ensureCapacityInternal(size + 1); // 容量检查
    elementData[size++] = e; // 添加元素
    return true;
}

四、remove方法执行流程

public E remove(int index) {
    rangeCheck(index); // 索引范围检查
    modCount++;
    E oldValue = elementData(index);
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    elementData[--size] = null; // 清除引用,帮助GC
    return oldValue;
}

执行流程:

  1. 检查索引是否越界(rangeCheck)
  2. 修改计数器递增(modCount++)
  3. 获取被删除元素
  4. 计算需要移动的元素数量
  5. 使用System.arraycopy移动后续元素
  6. 将最后一个位置置空并调整size
  7. 返回被删除元素

五、扩容机制核心代码

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5倍扩容
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

五、扩容示例验证

// 测试代码
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
    list.add(i);
    System.out.println("第"+(i+1)+"次添加,当前容量:" 
        + getCapacity(list));
}

// 反射获取真实容量
static int getCapacity(ArrayList<?> list) throws Exception {
    Field field = ArrayList.class.getDeclaredField("elementData");
    field.setAccessible(true);
    return ((Object[]) field.get(list)).length;
}

七、迭代器实现原理

private class Itr implements Iterator<E> {
    int cursor;       // 下一个元素索引
    int lastRet = -1; // 最后访问元素索引
    int expectedModCount = modCount;

    public boolean hasNext() {
        return cursor != size;
    }

    public E next() {
        checkForComodification();
        // ... 实际获取元素逻辑
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

八、性能对比数据

初始容量添加10万元素耗时(ms)
默认1035
指定1000008

六、扩容规律总结

元素数量实际容量
1010
1115
1622
2333
3449

通过源码分析可见,ArrayList采用1.5倍系数扩容,在空间和时间效率之间取得平衡。开发中应根据业务场景合理设置初始容量,避免频繁扩容带来的性能损耗。

参考资料


本文详细介绍了 ArrayList核心源码解析与扩容机制详解,希望对您有所帮助。如果您有任何问题,欢迎在评论区讨论!


文章作者: lucky845
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 lucky845 !
评论
  目录