ArrayList扩容本质是按需增长的数组封装,通过创建更大数组并复制数据实现动态性,在空间效率与时间开销间平衡;初始容量影响首次扩容时机,无参构造延迟初始化为10,指定容量可避免多次扩容;扩容发生在add等写入操作前,判断是否size等于数组长度;新容量默认1.5倍增长(oldCapacity + oldCapacity>>1),不足时直接取所需最小容量;扩容通过Arrays.copyOf创建新数组、复制有效元素、替换引用完成,时间复杂度O(n)。

Java中ArrayList的扩容机制,本质是“按需增长的数组封装”——它用固定数组做底层,靠自动创建更大数组+复制数据来模拟动态性。核心不是无限扩容,而是在空间效率和时间开销之间做平衡:每次扩容都尽量减少后续触发次数,又避免一次性分配过多内存。
初始容量怎么定?影响第一次扩容时机
无参构造(new ArrayList())在JDK 8+中采用延迟初始化:elementData初始指向一个空数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),真正分配空间发生在第一次add()时,直接设为10。 如果明确知道要存N个元素,用new ArrayList(N)可跳过初期扩容。比如预估存500条日志,初始化时指定500,就能避免从10→15→22→33…一路扩到500的过程。
什么时候会扩容?只在“真要放不下”时才动
扩容不是定时发生,而是在add()、add(int index, E)、addAll()等写入操作执行前检查容量是否够用。关键判断逻辑是:
- 单个添加:若 size == elementData.length,说明数组已满,下一次添加必扩容
- 批量添加(addAll):先算出总需容量 size + 集合大小,再与当前长度比较;若不足,直接按需扩容,不走1.5倍套路(例如空list加20个元素,首次就扩到20,不是先到10再扩)
新容量怎么算?1.5倍是默认,但不是铁律
扩容主逻辑在grow()方法里,计算分两步:
- 先尝试:newCapacity = oldCapacity + (oldCapacity >> 1) —— 即旧容量右移1位(等价于÷2),再相加,实现1.5倍增长。比如10→15,15→22(15/2=7.5,取整为7,15+7=22)
- 再兜底:如果算出的newCapacity仍小于实际需要的最小容量(minCapacity),就直接用minCapacity。这是为防止小容量初始值(如new ArrayList(1))反复微扩,也保障大批量插入时一步到位
扩容具体干了什么?换数组+搬数据
扩容不是修改原数组长度(Java数组长度不可变),而是三步原子操作:
立即学习“Java免费学习笔记(深入)”;
- 调用 Arrays.copyOf(elementData, newCapacity) 创建新数组
- 把原elementData中所有有效元素(索引0到size-1)复制过去
- 用新数组替换elementData引用,旧数组等待GC回收
这一步时间复杂度是O(n),所以频繁扩容会明显拖慢性能——这也是建议预设合理初始容量的根本原因。










