Deque 接口继承自 Queue接口,Queue 也是 Java 集合框架中定义的一种接口,直接继承自 Collection 接口。Deque 支持同时从两端添加或移除元素,因此又被成为双端队列。
Deque 和 Queue 方法的的对应关系如下:
Queue Method | Deque Method |
---|---|
add(e) | addLast(e) |
offer(e) | offerLast(e) |
remove() | removeFirst() |
poll() | pollFirst() |
element() | getFirst() |
peek() | peekFirst() |
Deque 和 Stack 方法的对应关系如下:
Stack Method | Deque Method |
---|---|
push(e) | addFirst(e) |
pop() | removeFirst() |
peek() | peekFirst() |
在 ArrayDeque 底部是使用数组存储元素,同时还使用了两个索引来表征当前数组的状态,分别是 head 和 tail。head 是头部元素的索引,但注意 tail 不是尾部元素的索引,而是尾部元素的下一位,即下一个将要被加入的元素的索引。
transient Object[] elements;
addLast方法
public void addLast(E e) {
if (e == null)
throw new NullPointerException();
//tail 中保存的是即将加入末尾的元素的索引
elements[tail] = e;
//tail 向后移动一位
//把数组当作环形的,越界后到0索引
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
//tail 和 head相遇,空间用尽,需要扩容
doubleCapacity();
}
(tail + 1) & (elements.length - 1)就能保证按照环形取得正确的下一个索引值的原因在于:
- length = 2^n,二进制表示为: 第 n 位为1,低位 (n-1位) 全为0
- length - 1 = 2^n-1,二进制表示为:低位(n-1位)全为1
- 如果 tail + 1 <= length - 1,则位与后低 (n-1) 位保持不变,高位全为0
- 如果 tail + 1 = length,则位与后低 n 全为0,高位也全为0,结果为 0
addFirst方法
public void addFirst(E e) {
if (e == null) //不支持值为null的元素
throw new NullPointerException();
elements[head = (head - 1) & (elements.length - 1)] = e;
if (head == tail)
doubleCapacity();
}
扩容方法:doubleCapacity
private void doubleCapacity() {
assert head == tail; //扩容时头部索引和尾部索引肯定相等
int p = head;
int n = elements.length;
//头部索引到数组末端(length-1处)共有多少元素
int r = n - p; // number of elements to the right of 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;
}