ArrayList扩容本质是为解决数组长度固定与size动态增长的矛盾,每次add都检查是否需扩容,通过位运算计算1.5倍新容量并兜底至最小需求,最后新建数组并复制元素。

要真正搞懂 ArrayList 扩容原理,不能只记“默认容量10、扩容1.5倍”这句口诀。得从它 为什么扩容、什么时候扩容、怎么算新容量、扩容干了什么 四个层面串起来理解。
扩容的底层动因:数组长度固定,但 size 要增长
ArrayList 底层是 Object[] elementData,是个普通数组——创建时长度就定死了。而 size 是实际元素个数,会随着 add() 一直加。一旦 size + 1 > elementData.length,数组就装不下了。这不是“建议扩容”,是 必须扩容,否则会数组越界。所以扩容本质是:用一次内存重分配,换后续几十次 add 的顺利执行。
触发时机很明确:每次 add 都检查 size+1 是否超限
不是“攒够一批才扩”,而是 每调用一次 add(E e),都立即判断是否需要扩容。核心逻辑在 ensureCapacityInternal(size + 1) 中:
- 如果当前 elementData 是空数组(无参构造),第一次 add 就直接设最小容量为 10(DEFAULT_CAPACITY)
- 否则,只要 size + 1 > elementData.length,就进 grow() 流程
- addAll 等批量方法也一样,内部仍是逐个 add,同样逐次触发检查
新容量怎么算:1.5 倍是位运算,但有兜底逻辑
grow() 方法里计算新容量的代码是:int newCapacity = oldCapacity + (oldCapacity >> 1);
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- oldCapacity >> 1 就是右移一位,等价于除以 2(整数截断),所以确实是“旧容量 × 1.5 向下取整”
- 但关键细节:如果算出来的 newCapacity 还是小于当前所需的最小容量(比如第一次 add 100 个元素,minCapacity=100),那就直接用 minCapacity,不硬撑 1.5 倍
- 另外,JDK 8 对容量上限做了保护,避免溢出,超过 Integer.MAX_VALUE - 8 会走特殊逻辑
扩容真正干了三件事:算、建、拷
很多人以为扩容就是“变大一点”,其实它是一次完整的内存操作:
- 算:确定最终 newCapacity(考虑初始空数组、1.5 倍、minCapacity 三重校验)
- 建:new Object[newCapacity],分配一块全新连续内存
- 拷:System.arraycopy(elementData, 0, newElementData, 0, size),把老数组所有有效元素复制过去;原数组失去引用,变成待 GC 对象
这个过程耗时主要在第三步——数据量越大,复制越慢,且频繁扩容还会制造大量短命对象,加重 GC 压力。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










