Java ArrayList 动态扩容本质是用新数组替换旧数组,触发条件为 add 时 size+1 > elementData.length;首次扩容至10,后续按1.5倍(oldCapacity + oldCapacity>>1)增长;扩容执行 Arrays.copyOf 复制元素、更新 elementData 引用及 modCount;预设初始容量可避免频繁扩容。

Java ArrayList 的动态扩容,本质是“用新数组替换旧数组”,不是真正改变原数组长度——因为 Java 数组本身不可变。关键在于它通过内部 elementData 数组 + 容量检查 + 数组复制三步协同,模拟出“可伸缩”的效果。
扩容什么时候发生?
每次调用 add(E e) 方法时,都会先检查:当前元素个数(size)加 1 是否超过底层数组长度(elementData.length)。一旦 size + 1 > elementData.length,就触发扩容流程。
- 首次添加元素时(JDK 8+),若用无参构造,elementData 是空数组({}),此时直接扩容到默认容量 10
- 后续添加中,只要空间不够,就进入 grow() 方法计算并创建新数组
- 批量添加(如 addAll)也会走同一套容量校验逻辑,避免逐个 add 导致多次扩容
新容量怎么算?核心是 1.5 倍,但有细节
扩容不是简单乘以 1.5,而是通过位运算高效实现:newCapacity = oldCapacity + (oldCapacity >> 1),即旧容量加上它的一半。
- 初始为 10 → 扩容后是 15(10 + 5)
- 15 → 22(15 + 7,向下取整)
- 22 → 33(22 + 11)
- 注意:早期资料提到“小于 64 时翻倍加 2”,那是过时描述;JDK 7 及以后统一采用 1.5 倍策略
- 如果计算出的 1.5 倍仍小于实际所需最小容量(比如一次 addAll 传入 100 个元素),则直接取所需容量
扩容具体做了什么?三步不可少
扩容不是魔法,而是明确的内存操作:
- 调用 Arrays.copyOf(elementData, newCapacity) 创建新数组,并把旧数组所有元素复制过去
- 将 elementData 引用指向这个新数组
- 更新 modCount(用于迭代器快速失败机制),防止并发修改不被发现
这个过程涉及内存分配和数据搬运,属于相对耗时操作,因此频繁扩容会影响性能。
怎么避免不必要的扩容?
如果你预先知道大概要存多少元素,可以主动干预初始化和预分配:
- 构造时指定合理初始容量:new ArrayList(200)
- 已有集合要转成 ArrayList,优先用带 Collection 参数的构造器:new ArrayList(sourceList),它会按 sourceList.size() 初始化容量
- 批量添加前,调用 ensureCapacity(int minCapacity) 预留空间,比如预计追加 500 个元素,就 ensureCapacity(size() + 500)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











