ArrayDeque扩容采用“左移一位加1”而非简单乘2,是为了在保证数组长度为2的幂的前提下避免整数溢出,并支持安全计算循环索引。

Java 中 ArrayDeque 的扩容不是通过传统“倍增”(如 oldCapacity * 2)实现的,而是采用**按位左移一位 + 1** 的方式,本质是寻找**大于当前容量的最小 2 的幂**,但仅限于数组长度为 2 的幂这一约束下进行扩容。这个逻辑封装在私有方法 doubleCapacity() 中。
为什么不用简单乘以 2?
ArrayDeque 内部要求底层数组长度必须是 2 的幂(如 4、8、16、32…),这是为了高效计算循环索引:用 index & (elements.length - 1) 替代取模运算 % elements.length。如果直接 oldCap * 2,当 oldCap 已是 2 的幂时结果仍是 2 的幂,看似可行;但问题出在**初始容量和溢出边界**上:
- 空 deque 默认数组长度为 16(2⁴),没问题;
- 但若当前容量是
Integer.MAX_VALUE / 2 + 1(即 1073741825),乘 2 就溢出成负数; - 而
doubleCapacity()使用位运算,能安全处理接近Integer.MAX_VALUE的情况,并保证结果仍为 2 的幂(或触发异常)。
doubleCapacity() 的核心逻辑
该方法接收原数组 elements,先检查是否已满(head == tail),再计算新容量:
- 获取当前长度
int n = elements.length; - 计算
int newCapacity = n (等价于 <code>n * 2); - 若
newCapacity (说明左移溢出,n ≥ 2³¹),抛出 <code>IllegalStateException; - 否则,创建长度为
newCapacity的新数组(必为 2 的幂,因原 n 是 2 的幂)。
注意:它**没有做“向上取整到 2 的幂”的通用计算**(比如对非 2 的幂输入),因为 ArrayDeque 始终维护数组长度为 2 的幂,所以 n 永远是 2 的幂,n 自然也是。
立即学习“Java免费学习笔记(深入)”;
扩容时的数据迁移细节
扩容不只是新建数组,还要把旧元素按循环顺序重新摆放:
- 旧数组中元素逻辑上是连续的(从
head到tail-1,跨尾部则折回头部); - 新数组中,所有元素被复制到起始位置(索引 0 开始),
head重置为 0,tail设为元素个数; - 这样就消除了“循环绕回”,让后续操作更简洁(例如
addLast直接写入tail位置)。
实际调用时机
doubleCapacity() 在以下情况被触发:
-
addFirst()或addLast()时发现队列已满(head == tail); - 此时先调用
doubleCapacity()扩容,再插入元素; - 扩容后容量翻倍,空间足够容纳当前所有元素 + 1 个新元素。
不复杂但容易忽略:它只解决“满”问题,不主动缩容;且整个过程无并发保护,ArrayDeque 不是线程安全的。


















