ArrayList扩容触发条件是执行add或addAll等操作且size等于elementData.length;默认扩容为1.5倍,即newCapacity = oldCapacity + (oldCapacity >> 1),再与minCapacity和MAX_ARRAY_SIZE比较取合适值。

ArrayList扩容触发条件是什么
扩容发生在执行 add 或 addAll 等修改结构的操作时,且当前元素个数 size 已等于底层数组 elementData.length。注意:构造时指定初始容量(如 new ArrayList(10))不会触发扩容,只有真正“放不下”时才触发。
默认扩容公式是怎样的
Java 8+ 的扩容逻辑是:newCapacity = oldCapacity + (oldCapacity >> 1),即扩大为原容量的 1.5 倍(右移 1 位等于除以 2)。但这个值不是最终结果——它还要和最小需要容量 minCapacity 比较,取较大者;再与 MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8)比较,防止溢出。
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);为什么扩容后不是直接翻倍
1.5 倍是权衡空间利用率与复制开销的结果。翻倍(×2)会导致内存浪费更明显,尤其在频繁 add 后又长期不增长的场景;而 1.5 倍能在多数情况下减少扩容次数,同时控制数组膨胀节奏。另外,JVM 数组分配对大对象有额外开销,过快增长可能触发更多 GC。
- 初始容量为 10 → 扩容后为 15 → 再扩容为 22 → 接着是 33、49…
- 若用 ×2,10→20→40→80,同样存 50 个元素需 4 次扩容;1.5 倍只需约 3 次(10→15→22→33→49)
-
ensureCapacity可主动预扩容,避免运行时多次 copy
扩容时的数组复制开销怎么评估
每次扩容都会调用 Arrays.copyOf,本质是 System.arraycopy,时间复杂度 O(n),其中 n 是旧数组长度。这意味着:频繁在末尾 add 小量数据(如循环逐个 add)比一次性 addAll 多次触发扩容更慢。
立即学习“Java免费学习笔记(深入)”;
- 避免在循环内反复 add:改用
new ArrayList(expectedSize)预设容量 - 已知要加 1000 个元素,却用无参构造,会经历约 10 次扩容(10→15→22→33→49→73→109→163→244→366→549→823)
-
trimToSize()可释放冗余空间,但仅在确定不再增容后调用,否则后续 add 又触发扩容
实际开发中,最容易被忽略的是:扩容判断只看 size == elementData.length,不关心是否删除过元素。也就是说,即使你调用过 remove 让 size 变小,只要没手动 trimToSize,底层数组依然维持最大扩容后的长度——这会影响内存占用,尤其在长生命周期的 list 中反复增删时。










