ArrayList 的动态扩容是其核心特性之一,而扩容行为并非每次 add() 都触发,而是按需、批量增长。下面结合源码,逐步还原其扩容全过程。
✅ 场景一:添加第 1 个元素(首次扩容)
初始状态:elementData = [](空数组),size = 0
调用 add(e) → ensureCapacityInternal(1)
因使用无参构造,elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 成立
minCapacity = max(10, 1) = 10
当前 elementData.length = 0,10 > 0 → 触发 grow(10)
执行 grow():
oldCapacity = 0
newCapacity = 0 + (0 >> 1) = 0
但 newCapacity (0) < minCapacity (10) → 修正为 10
最终:elementData = new Object[10]
📌 结果:首次添加即分配 10 个容量,避免后续频繁扩容。
✅ 场景二:添加第 2 ~ 第 10 个元素(无需扩容)
此时 elementData.length = 10,size 从 1 增至 9
每次 add() 计算 minCapacity = size + 1(最大为 10)
minCapacity (≤10) ≤ elementData.length (10) → 不进入 grow()
直接赋值:elementData[size++] = e
📌 结果:连续 9 次添加,零扩容,高效!
✅ 场景三:添加第 11 个元素(第二次扩容)
size = 10,调用 add() → minCapacity = 11
elementData.length = 10,11 > 10 → 触发 grow(11)
执行 grow():
oldCapacity = 10
newCapacity = 10 + (10 >> 1) = 10 + 5 = 15
15 ≥ 11 → 无需修正
最终:elementData = Arrays.copyOf(..., 15)
📌 结果:容量从 10 → 15,增长 1.5 倍
🔍 核心:grow() 方法逻辑
java
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)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
扩容因子:oldCapacity >> 1 等价于 /2,所以新容量 = 1.5 × oldCapacity
位运算优化:比除法更快
安全兜底:若 1.5 倍仍不够(如批量添加大量元素),直接用 minCapacity
💡 性能建议
元素数量 默认扩容次数 推荐做法
100 ~4 次(10→15→22→33→49→73→109) new ArrayList<>(100)
1000 ~8 次 new ArrayList<>(1000)
预设初始容量可完全避免扩容开销,尤其在循环或批量处理场景中至关重要。
总结
ArrayList 首次扩容到 10;
后续每次扩容为 当前容量的 1.5 倍;
扩容本质是 创建新数组 + 复制旧数据,代价较高;
最佳实践:预估大小,显式指定初始容量。
理解这一机制,不仅能写出高性能代码,更能深入体会 Java 集合设计中的“空间换时间”智慧。