ArrayList扩容触发条件是size == elementData.length,即每次add前检查是否需扩容;首次扩容从0→10,后续按1.5倍增长;仅能通过initialCapacity或ensureCapacity干预,无法修改触发阈值。

ArrayList 的扩容触发条件是 size == elementData.length
Java 中 ArrayList 并不预先按固定比例(如 1.5 倍)检查是否扩容,而是在每次调用 add(E) 方法时,先判断当前元素个数 size 是否已等于底层数组 elementData.length。只有相等时,才触发扩容逻辑。
这意味着:即使你初始化时传入初始容量(如 new ArrayList(10)),只要 size 还没达到 10,就不会扩容;一旦第 11 次 add(),就必然扩容。
- 扩容前的判断代码本质是:
if (size == elementData.length) grow(); - 该判断发生在
add(E)开头,**不是在插入后检查**,所以不会出现“插完才爆数组越界” - 对
add(int index, E element)同样适用——只要插入后size将超过当前数组长度,就提前扩容
默认扩容策略是 oldCapacity + (oldCapacity >> 1),但首次扩容可能不同
扩容实际调用的是 grow(int minCapacity),其核心逻辑是:newCapacity = oldCapacity + (oldCapacity >> 1)(即 1.5 倍)。但这个公式只在 oldCapacity > 0 时生效。
关键例外:空 ArrayList(例如 new ArrayList())首次添加元素时,elementData 是空数组 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,此时 oldCapacity == 0,会直接设为 DEFAULT_CAPACITY(即 10)。
- 首次扩容:从 0 → 10(不是 0 → 0×1.5)
- 第二次扩容:10 → 15(10 + 10>>1)
- 第三次:15 → 22(15 + 15>>1 = 15 + 7 = 22)
- 注意:
>>1是右移一位,等价于整除 2,向下取整
手动控制扩容阈值只能靠预设 initialCapacity 或 ensureCapacity
你无法修改 ArrayList 的扩容触发点(比如改成 size == elementData.length - 2 就扩容),JDK 没提供 hook 或 protected 扩容入口。能干预的只有两个时机:
- 构造时指定
initialCapacity:如new ArrayList(64),让前 64 次add()完全不触发扩容 - 运行中调用
ensureCapacity(int minCapacity):主动要求底层数组至少达到某长度,若不足则立即扩容(同样走 1.5 倍逻辑) - 避免误用
trimToSize():它只会缩容到size,不影响扩容阈值逻辑
扩容本身有性能开销,频繁触发会导致数组复制和 GC 压力
每次扩容都要新建数组、用 System.arraycopy() 复制旧元素。虽然单次很快,但若在循环中无预估地反复 add(),可能连续触发多次扩容(比如从 10→15→22→33→49→73…),造成 O(n²) 级别的复制总量。
- 典型坏模式:
ArrayList list = new ArrayList(); for (int i = 0; i —— 触发约 7 次扩容 - 优化写法:
ArrayList list = new ArrayList(1000);或list.ensureCapacity(1000); - 注意:
ensureCapacity()不改变size,仅确保底层数组够大,后续add()仍按原逻辑判断
真正需要关注的不是“怎么改阈值”,而是预估数据规模并前置分配——因为扩容逻辑被封装死,任何反射或子类重写都破坏兼容性且不可靠。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











