arraylist扩容的1.5倍通过位运算oldcapacity + (oldcapacity >> 1)实现,非硬编码;首次扩容默认为10,后续才用该公式;位运算高效、无精度问题;真正开销在system.arraycopy复制数据。

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 = 1→1 + (1 >> 1) == 1 + 0 == 1(不扩容,合理) -
oldCapacity = 2→2 + 1 == 3 -
oldCapacity = 10→10 + 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 倍逻辑。










