arraylist的“动态扩容”本质是数组拷贝与容量重算:底层为object[] elementdata(容量)和size(实际元素数);add时若size+1>elementdata.length即触发扩容;默认新容量=旧容量×1.5,不足则兜底为所需最小容量,上限为integer.max_value-8。

ArrayList 的“动态扩容”不是魔法,而是用数组拷贝+容量重算实现的自动管理。它底层始终是一块连续的 Object[] 内存,所谓“动态”,只是把“数组满了就换更大的”这个过程封装在了 add 方法里,你不用手动复制、不用判断长度,但得知道它什么时候换、怎么换、为什么这么换。
底层数组长什么样:elementData 和 size 是两回事
ArrayList 有两个关键字段:
-
elementData:真正存数据的 Object 数组,它的
.length就是当前容量(capacity); -
size:当前已存的有效元素个数,
list.size()返回的就是它,不是数组长度。
比如执行 new ArrayList(20),elementData.length == 20,但 size == 0;加进 5 个元素后,size == 5,容量仍是 20,还没触发扩容。
扩容什么时候发生:看 size + 1 是否超过 elementData.length
每次调用 add(E e),内部先检查:if (size + 1 > elementData.length)。只要下一次插入会让元素个数超出当前数组长度,就立刻扩容。
注意:不是“填满才扩”,而是“再塞一个就溢出”时扩。所以第 11 个元素进一个初始容量为 10 的列表,就会触发第一次扩容。
扩容怎么扩:1.5 倍是默认策略,但会兜底保障
扩容核心逻辑在 grow() 方法中,分三步:
- 计算新容量:
int newCapacity = oldCapacity + (oldCapacity >> 1)(即旧容量 × 1.5,用位运算提速); - 兜底校验:如果算出来的新容量仍小于实际需要的最小容量(比如首次 add 且原数组为空),就直接用所需容量(例如一次 add 20 个,第一次就扩到 20,不走 10→15);
- 上限控制:不能超过
Integer.MAX_VALUE - 8(JVM 数组头信息预留空间),超了会抛OutOfMemoryError。
扩容本质就是:elementData = Arrays.copyOf(elementData, newCapacity) —— 创建新数组、复制老数据、替换引用。
初始容量怎么选:懒加载 vs 预分配
JDK 8+ 对无参构造做了优化:
-
new ArrayList():不立即分配数组,elementData指向共享空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA,直到第一次add才扩容到 10; -
new ArrayList(N):直接分配长度为 N 的数组,适合明确知道数据量的场景(如读取 1000 行 CSV,建议直接new ArrayList(1000)); - 传入集合构造:会先转成数组,再做类型适配(确保是
Object[]),然后赋值给elementData。
预设合理初始容量,能显著减少扩容次数,避免反复拷贝带来的性能损耗。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











