登录
首页 >  文章 >  java教程

ArrayList扩容为何增长1.5倍

时间:2026-04-17 12:53:33 465浏览 收藏

ArrayList的扩容并非简单硬编码“1.5倍”,而是通过高效无误差的位运算`oldCapacity + (oldCapacity >> 1)`实现,既规避了浮点运算的性能开销与舍入风险,又保证了跨平台一致性;首次扩容默认为10,后续才启用该公式;真正影响性能的是`System.arraycopy`导致的O(n)数据复制开销和GC压力,而1.5倍是空间利用率与扩容频率之间的工程权衡——与其纠结修改这一固定策略,不如善用带参构造器预估容量,从源头避免频繁扩容。

如何掌握ArrayList在触发扩容时增长为原容量1.5倍原理

ArrayList扩容时的1.5倍是怎么算出来的

不是硬编码写死的 1.5,而是通过位运算模拟乘法:旧容量右移一位(oldCapacity >> 1)再加回原值,等价于 oldCapacity + oldCapacity / 2。JDK 7 及之后的实现中,grow 方法里这行代码很关键:int newCapacity = oldCapacity + (oldCapacity >> 1)

注意:这是对非首次扩容而言;首次添加元素时,如果构造时没指定初始容量,会直接分配 10 个元素的空间,不走这个公式。

为什么不用浮点数或 Math.round

位运算是整数运算,零开销、无精度问题、跨平台一致。用 (int)(oldCapacity * 1.5) 看似直观,但涉及浮点乘法+强制转整,性能略差,且在极小容量(如 1)下可能因舍入误差导致结果异常(虽然实际影响不到,但设计上规避了所有不确定性)。

  • oldCapacity = 11 + (1 >> 1) == 1 + 0 == 1(不扩容,合理)
  • oldCapacity = 22 + 1 == 3
  • oldCapacity = 1010 + 5 == 15

扩容后数组复制的实际开销在哪

真正耗时的是 Arrays.copyOf 调用底层 System.arraycopy 复制整个老数组内容——这是 O(n) 操作,且触发 GC 压力。1.5 倍是平衡空间浪费和扩容频次的经验值:比 2 倍省内存,又比 1.2 倍减少重复制次数。

容易忽略的一点:如果提前知道大概元素数量,用带参构造器初始化(如 new ArrayList(1000)),能完全避免前几次扩容。

自定义扩容策略不可行?

ArrayList 的扩容逻辑封装在私有方法 grow 中,且没有提供钩子或 protected 扩展点。继承 ArrayList 并重写方法无法生效,因为核心添加逻辑(如 add)内部直接调用私有 grow

若真需要定制增长因子,只能自己实现 List(基于 Object[] + 手动管理扩容),或者用 ArrayDeque(它用 2 倍扩容,但不保证顺序语义)替代——不过这就脱离了 ArrayList 的使用场景了。

真正该关注的,是预估容量和避免在循环中反复 add 导致多次扩容,而不是试图改写这个 1.5 倍逻辑。

以上就是《ArrayList扩容为何增长1.5倍》的详细内容,更多关于的资料请关注golang学习网公众号!

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>