arraylist扩容在add时size等于数组长度即触发,采用1.5倍位运算扩容(newcapacity = oldcapacity + (oldcapacity >> 1)),不足则取mincapacity,溢出走hugecapacity;代价为o(n)数组复制,但均摊o(1);全程非线程安全。

面试中谈 ArrayList 扩容机制,关键不是背代码,而是讲清“为什么这样设计”+“怎么一步步发生的”+“哪些细节容易踩坑”。只要逻辑清晰、例子具体、语气笃定,就能显得很扎实。
扩容触发时机:不是满才扩,是“要放不下时”才扩
ArrayList 的扩容发生在 add() 方法中,且仅当 size == elementData.length 时触发。注意不是“满了之后下一次 add 才扩”,而是“这次 add 发现放不下了,立刻扩完再放”。
比如容量为 10 的 list 已存 10 个元素,调用 add 第 11 个元素时,会先扩容再插入——整个过程对调用方透明。
扩容算法:1.5 倍 + 向上取整,但有边界保护
新容量计算公式是:newCapacity = oldCapacity + (oldCapacity >> 1)(即 1.5 倍)。但有两个关键细节必须点出:
- 用位运算而非 *1.5,避免浮点和强制转换,更高效
- 如果计算结果小于所需最小容量(比如原容量 1,1+0=1 不够放第 2 个),就直接取 minCapacity;如果 newCapacity 溢出(超过 Integer.MAX_VALUE),则用 hugeCapacity() 处理——这里会尝试分配 Integer.MAX_VALUE 或 MAX_ARRAY_SIZE(通常为 Integer.MAX_VALUE - 8)
扩容代价:复制数组是唯一开销,但不是每次 add 都发生
扩容本质是 Arrays.copyOf(elementData, newCapacity),也就是新建数组 + System.arraycopy。这是 O(n) 操作,但因为扩容频率随容量增大而降低(1→2→3→4→6→9→13…),所以均摊时间复杂度仍是 O(1)。
可以顺带提一句:如果能预估大小,用 new ArrayList(initialCapacity) 构造,能避免多次扩容,是常见优化手段。
线程安全?别掉坑:扩容过程完全非线程安全
整个扩容流程(判断、计算、新建、复制、赋值)不是原子操作。多线程并发 add 可能导致:
– 数组复制不完整
– 新旧引用错乱(如 size 已更新但 elementData 还没换)
– 甚至 NullPointerException 或数据丢失
所以必须强调:ArrayList 扩容不是线程安全的,高并发场景要用 CopyOnWriteArrayList 或外部同步。
把这四点串起来说,配合一个简单例子(比如初始容量 2,连续 add 5 个数,说明扩容节点和新容量变化),面试官基本就认可你真懂了。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











