java中arraylist采用1.5倍扩容是空间、时间与硬件特性综合权衡的工程最优解:兼顾内存复用与碎片控制、复制开销与扩容频次平衡、位运算效率与整数友好性,并支持动态适应实际需求。

Java 中 ArrayList 选择 1.5 倍扩容,不是凭经验拍板,而是经过空间、时间、硬件特性综合权衡后的工程最优解。
内存复用与碎片控制
1.5 倍扩容(即 newCapacity = oldCapacity + (oldCapacity >> 1))产生的容量序列(如 10→15→22→33→49→73…)具备近似斐波那契增长特性。这种增长方式让历史上释放的旧数组内存块,在后续某次扩容时更有可能被重新合并利用。而 2 倍扩容(10→20→40→80…)会持续产生无法被后续请求复用的大块空闲内存,长期运行下加剧堆内存碎片,增加 GC 压力。
复制开销与扩容频次的平衡
每次扩容都要调用 Arrays.copyOf(),本质是 System.arraycopy(),耗时为 O(n)。太小的增幅(比如 1.2 倍)会导致扩容次数剧增,总拷贝量上升;太大增幅(比如 2 倍)虽减少次数,但单次拷贝数据更多,且浪费大量未使用的内存空间。1.5 倍在二者间取得实测最优折中:
- 从 10 扩到 15,多占 5 个槽位,但避免了第 12、13、14 次 add 都触发扩容
- 从 15 扩到 22,增量 7,比 2 倍(+15)节省近一半冗余空间
- 整体上使平均每个元素承担的“扩容摊销成本”最低
位运算效率与整数友好性
公式里用的是 oldCapacity >> 1,而非 oldCapacity * 1.5 或 oldCapacity / 2。右移一位是 CPU 级别的原子操作,无浮点计算、无舍入误差、无除法开销。对任意整数 oldCapacity,oldCapacity + (oldCapacity >> 1) 总是整数结果,天然适配数组长度必须为 int 的约束,也规避了浮点乘法可能引入的精度问题。
实际扩容逻辑不止“固定倍率”
1.5 倍只是默认策略,不是铁律:
- 首次添加空列表时,直接设容量为 10,不走 1.5 倍计算
- 若一次 addAll(100) 且当前容量仅 30,grow 方法会跳过 1.5 倍计算,直接将 newCapacity 设为 100
- 扩容后若仍小于所需最小容量(minCapacity),就以 minCapacity 为准
本质上,1.5 倍是“尽力而为”的增长尝试,背后逻辑是:尽量少扩、尽量少拷、尽量省内存、尽量快计算——它不是一个数学常数,而是一套务实的动态适应机制。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











