ArrayDeque是Deque接口的子实现,是一个双端队列。
有序(读取顺序和写入顺序完全一致或完全相反)
可以存储null
可以存储重复元素
ArrayDeque的底层是一个循环队列,大大节约了空间。
底层数组的默认大小是16,当然也可以在创建对象的时候自定义容量。让我们来看一下这个队列底层是怎么实现创建数组和扩容的。
// 底层的循环队列
// transient表示不能被序列化
transient Object[] elements;
// 创建容量为16的Object数组
public ArrayDeque() {
elements = new Object[16];
}
// 调用offer方法插入新元素
// 在队列末尾插入元素
public boolean offer(E e) {
return offerLast(e);
}
public boolean offerLast(E e) {
addLast(e);
return true;
}
public void addLast(E e) {
// 不能在队列中插入null
if (e == null)
throw new NullPointerException();
// tail指向队列中下一个待插入的位置
// 队尾入队
elements[tail] = e;
// 底层循环队列采取的是牺牲一个存储空间来判断队列是否已满
// (tail + 1) & (elements.length - 1))相当于(tail+1)%len
// 判断当前队列是否满了
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
// 扩容2倍
doubleCapacity();
}
private void doubleCapacity() {
assert head == tail;
int p = head;
int n = elements.length;
int r = n - p; // number of elements to the right of p
// newCapacity=n*2
int newCapacity = n << 1;
// newCapacity超出int的最大正整数值
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;
}
默认容量16是2的幂次,所以每次扩容只要右移一位,就可以实现扩容一倍。
但是如果是自定义的非2的整数次幂的初始容量,底层是如何实现扩容的呢?
// 传入自定义的容量,创建对象
public ArrayDeque(int numElements) {
// 调用方法,初始化底层循环队列
allocateElements(numElements);
}
private void allocateElements(int numElements) {
elements = new Object[calculateSize(numElements)];
}
private static int calculateSize(int numElements) {
// 8
int initialCapacity = MIN_INITIAL_CAPACITY;
// 自定义的容量不超过8,则数组容量定位8
// 超过8,则重新生成一个离initialCapacity最近的2的整数次幂的值
if (numElements >= initialCapacity) {
initialCapacity = numElements;
// 假设initialCapacity最高位的1在第HB个位置上
// >>> 表示逻辑移位
// 先逻辑右移一位,再和原数进行或操作
// (后面的数我们先不管,只看前面的1)
// 右移使得最高位的1右移一位,‘|’ 操作使得这个1前面又出现了一个1
initialCapacity |= (initialCapacity >>> 1);
// 右移两位,再或运算,使得前面出现4个1
initialCapacity |= (initialCapacity >>> 2);
initialCapacity |= (initialCapacity >>> 4);
initialCapacity |= (initialCapacity >>> 8);
initialCapacity |= (initialCapacity >>> 16);
// 多次移位使得这个数从HB开始都是1
// +1,使得其变为2的HB次
initialCapacity++;
// 越界
if (initialCapacity < 0)
// 再逻辑右移一次,initialCapacity=2^30
initialCapacity >>>= 1;
}
return initialCapacity;
}
综上,当我们自定义ArrayDeque底层队列的容量时,如果传入的容量小于8,那么jvm就会创建一个容量为8的循环队列。如果传入的容量大于8,那么队列的容量就是离自定义值最接近(大于)的2的整数次幂。
ty;
}
综上,当我们自定义ArrayDeque底层队列的容量时,如果传入的容量小于8,那么jvm就会创建一个容量为8的循环队列。如果传入的容量大于8,那么队列的容量就是离自定义值最接近(大于)的2的整数次幂。