ArrayList扩容本质是用可控内存预分配替代频繁小块申请,触发于add时size等于数组长度,通过oldCapacity+oldCapacity>>1计算新容量,避免空间浪费与频繁复制,构造时预设容量可优化性能。

ArrayList 扩容机制的本质,是用可控的内存预分配换掉频繁的小块内存申请。结合源码看,它不是“盲目变大”,而是一套有节奏、可预测、能优化的内存管理策略。
扩容触发点在 add 方法里
每次调用 add(E e),都会先检查:if (size == elementData.length)
成立就走 grow() —— 这是扩容的唯一入口。也就是说,扩容只发生在真正“放不下”的那一刻,不是每加一个都动数组。
grow() 里的容量计算很讲究
核心逻辑是这行:int newCapacity = oldCapacity + (oldCapacity >> 1);
也就是旧容量加一半(位运算比除法快,也更省内存)。比如:
- 初始 10 → 扩到 15
- 15 → 扩到 22(15 + 7)
- 22 → 扩到 33(22 + 11)
这种非整倍数增长,既避免了像 2 倍扩容那样浪费空间,又比固定加 10 更少触发次数。
扩容代价在 copyOf,优化关键在“别让它发生”
Arrays.copyOf(elementData, newCapacity) 是真正的开销所在: - 要分配一块新内存 - 要把老数组所有元素逐个复制过去 - 老数组变成垃圾,等 GC 回收
所以内存分配优化的核心思路不是“怎么让 copy 快一点”,而是:
- 构造时预设容量:知道要存 5000 个元素,就写 new ArrayList(5000),跳过前 7~8 次扩容
- 批量添加用 addAll:比循环 add 更容易触发一次到位的扩容判断
- 必要时手动 ensureCapacity:比如循环前先调用 list.ensureCapacity(expectedSize),确保后续 N 次 add 都不扩容
JDK 8 的懒加载让首次分配更省
无参构造 new ArrayList() 并不直接创建长度为 10 的数组,而是赋值一个空数组 EMPTY_ELEMENTDATA;直到第一次 add 才真正分配 10 个长度。这对只读或可能为空的集合很友好,减少无效内存占用。
你是 TypeScript 项目架构专家,专注于脚手架搭建生产就绪的 Node.js 和前端应用。生成包含现代工具链(pnpm、Vite、Next.js)、类型
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










