arraylist扩容本质是创建新数组并复制数据,无参构造不立即分配空间,首次add才扩容至10;扩容触发条件为size+1>capacity,新容量按oldcapacity+(oldcapacity>>1)计算,核心开销在arrays.copyof的o(n)拷贝。

Java 数组本身不可扩容,ArrayList 的“扩容”其实是用新数组替换旧数组的过程。核心不是改变原数组长度,而是创建更大数组、复制数据、更新引用。理解这一点,就抓住了整个机制的起点。
ArrayList 初始化时到底分配了多少空间?
无参构造(new ArrayList())并不立即分配 10 个元素的空间。它只是把 elementData 指向一个共享的空数组 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,真正内存分配发生在第一次 add() 时——此时才扩容到默认容量 10。
-
new ArrayList(0):使用
EMPTY_ELEMENTDATA,首次添加按需扩(比如加 1 个就扩到 1) - new ArrayList(20):直接创建长度为 20 的数组,跳过初始扩容
- 所有构造方式都保证
size == 0,但底层数组长度(capacity)可能为 0、10 或指定值
扩容触发的准确条件是什么?
不是“数组满了才扩”,而是添加前就预判:当 size + 1 > elementData.length 时,说明再加一个元素就会越界,必须提前扩容。
- 这个判断在
add(E)内部的ensureCapacityInternal(size + 1)中完成 -
size是当前元素个数,elementData.length是当前容量,二者始终不同步 - 即使只存 1 个元素,只要 capacity 是 0(如
new ArrayList(0)),下一次 add 就会触发扩容
新容量怎么算?1.5 倍不是简单四舍五入
公式是 oldCapacity + (oldCapacity >> 1),即“原容量 + 原容量右移一位”。右移 1 位等价于整除 2,所以本质是 1.5 × oldCapacity,但结果是向下取整(因为是整数运算)。
- 从 10 → 15(10 + 5)
- 从 15 → 22(15 + 7,不是 7.5)
- 从 22 → 33(22 + 11)
- 如果计算出的新容量仍小于实际需要的最小容量(比如要加第 100 个元素但当前容量只有 60),则直接取最小需求值
扩容过程中的性能关键点在哪?
真正的开销不在计算新容量,而在 Arrays.copyOf(elementData, newCapacity) —— 这是一次完整的数组拷贝,时间复杂度 O(n),且会引发 GC 压力。
- 频繁 add 导致多次扩容 → 多次拷贝 → 性能明显下降
- 若已知最终数据量(如读取 5000 行 CSV),推荐初始化时指定容量:
new ArrayList(5000) - 避免在循环中反复调用
add()而不预估容量;更忌用remove()频繁删中间元素——每次删都要移动后续所有元素
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











