ArrayDeque是Java中基于数组实现的双端队列,很多开发者在使用时会发现它的容量表现和普通的ArrayList存在差异,甚至和官方文档的描述存在容易混淆的地方。要搞清楚它的真实容量,需要结合文档说明和底层源码一起分析。

ArrayDeque的官方文档容量说明
根据Java官方文档的描述,ArrayDeque的底层是一个可动态扩容的数组,默认的初始容量是16,并且这个容量会保证是2的幂次方。文档中提到,ArrayDeque会按需扩容,不会出现容量不足的情况,但并没有明确说明扩容后的具体容量计算规则,也没有说明容量和实际可存储元素数量的关系。
从源码看初始容量逻辑
我们先看ArrayDeque的构造方法相关源码,理解初始容量的计算规则:
// 默认无参构造,初始容量为16
public ArrayDeque() {
elements = new Object[16];
}
// 指定初始容量的构造方法
public ArrayDeque(int numElements) {
allocateElements(numElements);
}
// 分配元素的核心方法
private void allocateElements(int numElements) {
int initialCapacity = 8;
// 找到大于等于numElements的最小2的幂次方,且不小于8
if (numElements >= initialCapacity) {
initialCapacity = numElements;
initialCapacity |= (initialCapacity >>> 1);
initialCapacity |= (initialCapacity >>> 2);
initialCapacity |= (initialCapacity >>> 4);
initialCapacity |= (initialCapacity >>> 8);
initialCapacity |= (initialCapacity >>> 16);
initialCapacity++;
// 处理溢出情况,如果超过int最大值,回退到2的30次方
if (initialCapacity < 0) {
initialCapacity >>>= 1;
}
}
elements = new Object[initialCapacity];
}
从源码可以看到,当使用指定初始容量的构造方法时,ArrayDeque不会直接使用传入的参数作为数组长度,而是会计算大于等于该参数的最小2的幂次方,且最小不会小于8。比如传入10,最终数组长度会是16;传入20,最终数组长度会是32。
真实容量与可存储元素数量的关系
很多开发者会误以为数组的长度就是ArrayDeque的真实容量,也就是最多能存储的元素数量,但实际上ArrayDeque为了保证双端队列的高效操作,会预留一个数组位置不使用。我们可以通过扩容的源码来验证这一点:
// 扩容方法,当元素数量达到数组长度-1时触发
private void doubleCapacity() {
int p = head;
int n = elements.length;
int r = n - p;
int newCapacity = n << 1;
if (newCapacity < 0) {
throw new IllegalStateException("Sorry, deque too big");
}
Object[] a = new Object[newCapacity];
System.arraycopy(elements, p, a, 0, r);
System.arraycopy(elements, 0, a, r, p);
elements = a;
head = 0;
tail = n;
}
在ArrayDeque中,tail指针指向的是下一个可插入元素的位置,当tail == head且数组不是空的时候,就说明队列已满,此时会触发扩容。而初始状态下head和tail都为0,当插入第一个元素后tail变为1,所以当数组长度为n时,最多只能存储n-1个元素,这就是它的真实可存储容量。
不同场景下的容量验证
我们可以通过简单的测试代码验证上述结论:
import java.util.ArrayDeque;
public class ArrayDequeTest {
public static void main(String[] args) {
// 测试默认构造的容量
ArrayDeque<Integer> deque1 = new ArrayDeque<>();
// 反射获取底层数组长度
java.lang.reflect.Field elementsField = ArrayDeque.class.getDeclaredField("elements");
elementsField.setAccessible(true);
Object[] elements1 = (Object[]) elementsField.get(deque1);
System.out.println("默认构造底层数组长度:" + elements1.length); // 输出16
// 测试指定容量为10的情况
ArrayDeque<Integer> deque2 = new ArrayDeque<>(10);
Object[] elements2 = (Object[]) elementsField.get(deque2);
System.out.println("指定容量10的底层数组长度:" + elements2.length); // 输出16
// 测试可存储元素数量
ArrayDeque<Integer> deque3 = new ArrayDeque<>(8);
Object[] elements3 = (Object[]) elementsField.get(deque3);
System.out.println("指定容量8的底层数组长度:" + elements3.length); // 输出8
// 尝试插入8个元素
for (int i = 0; i < 8; i++) {
deque3.add(i);
}
System.out.println("插入8个元素后是否触发扩容:" + (deque3.size() == 8)); // 输出false,实际插入7个就会满
}
}
总结
ArrayDeque的真实容量需要从两个维度理解:底层数组的长度始终是2的幂次方,最小为8;而实际可存储的元素数量是底层数组长度减1。文档中提到的容量更多是指底层数组的长度,而开发者在使用时更关注的是可存储元素的数量,需要明确两者的差异。在开发中如果需要预估ArrayDeque的容量,建议按照需要存储的元素数量加1后取最近的2的幂次方来指定初始容量,避免不必要的扩容操作。
ArrayDequeJava集合双端队列容量扩容修改时间:2026-07-16 13:54:31