arraylist扩容机制核心是“检查→计算→迁移→更新引用”闭环:add时先判断size+1>elementdata.length触发扩容;新容量默认为旧容量1.5倍(oldcapacity + oldcapacity>>1),不足则取所需最小容量,溢出时兜底至integer.max_value-8;最后通过arrays.copyof复制元素并更新elementdata引用。

ArrayList 的扩容机制常考,核心就三点:触发条件、新容量计算、数组复制。闭环逻辑在于“检查→计算→迁移→更新引用”,每一步都不可省略,且必须按序执行。
扩容触发的判断逻辑
每次 add(E e) 时,先调用 ensureCapacityInternal(size + 1),本质是检查当前 elementData 数组是否还能容纳新元素。关键不是看 size 是否等于 capacity,而是看 size + 1 > elementData.length —— 这个“+1”容易被忽略,却是理解边界条件的关键。
- 初始空 ArrayList(无参构造):elementData 指向一个空 Object[](EMPTY_ELEMENTDATA),首次 add 才真正触发扩容
- 显式指定初始容量:elementData 直接分配对应长度数组,此时 size=0,但 length > 0,add 第 1 个元素不扩容
- 注意:size 是实际元素个数,length 是数组长度,二者语义不同,不能混用
新容量的计算规则
扩容不是简单 ×2,而是分两步算:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 先尝试 oldCapacity + (oldCapacity >> 1)(即 1.5 倍),这是默认策略
- 但如果 1.5 倍仍不够(比如极小初始容量或大批量 addAll),则直接取所需最小容量:Math.max(minCapacity, oldCapacity + (oldCapacity >> 1))
- 最终还要兜底:若计算结果溢出(如超过 Integer.MAX_VALUE),则用 hugeCapacity(minCapacity) 处理——这里会返回 Integer.MAX_VALUE 或 MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8),避免 OutOfMemoryError
扩容后的数据迁移与引用更新
扩容本质是创建新数组 + 复制旧数据 + 替换引用,三步缺一不可:
- 调用 Arrays.copyOf(elementData, newCapacity),底层是 System.arraycopy,高效但仍是 O(n) 时间操作
- 复制完成后,elementData = newElementData,原数组失去引用,等待 GC
- 注意:modCount 在 add 过程中会自增,用于 fail-fast 检测,扩容本身不单独改变 modCount
为什么说这是“闭环”?
因为整个流程形成完整因果链:add → size+1 超限 → 触发 ensureCapacity → 计算 newCapacity → 创建新数组 → 复制 → 更新 elementData → add 继续完成。任意一环缺失(比如忘了更新 elementData 引用),就会导致后续操作仍在旧数组上,引发 ArrayIndexOutOfBoundsException 或数据丢失。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










