数组双端队列 ArrayDeque
双端队列,表示可以添加元素到(或删除,获取)队列头也可以添加元素到(或删除,获取)队列尾
1. 底层数据结构
类中定义成员变量,一个数组和两个int
transient Object[] elements;
transient int head;
transient int tail;
数据结构比较清晰,就是一个数组,head指向队列的头,tail指向队列的尾
数组定义要求数组的容量为2的n次幂
2. 常见接口实现方式
删除元素
先看删除逻辑,因为比较简单,实现如下
public E poll() {
if (size == 0) // 队列为空
return null;
int s = --size;
modCount++;
// 数组中第一个为队列头
E result = (E) queue[0];
E x = (E) queue[s];
queue[s] = null;
if (s != 0)
// 队列非空时,重排剩下的元素
siftDown(0, x);
return result;
}
添加元素
在队头和队尾添加的逻辑基本一致,这里以在队列尾添加元素绩进行分析
public void addLast(E e) {
if (e == null) // 不支持向队列中塞入null对象
throw new NullPointerException();
elements[tail] = e;
if ( (tail = (tail + 1) & (elements.length - 1)) == head) {
// tail 后移一位,若tail长度超过数组长度,则tail转到数组头
// 上面的与操作等价于对数组长度进行求余
// 若tail和head相等,则表示数组内容填充满,需要扩容
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
// 新的容量为原来的两倍
int newCapacity = n << 1;
if (newCapacity < 0) // int逸出判断
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;
}
下面是一个数组扩容的逻辑示意图
- 图中上面的为原数组,添加元素8之后,tail和head相等,都是5
- 原队列容量为8,顺序为
1 -> 9 -> 5 -> 7 -> 4 -> 3 -> 5 -> 8
- 扩容后,数组容量为16,顺序保持不变,head为0,tail为8
弹出元素
以弹出队头的元素为例,会直接返回队列头的元素,并将head前移一位,并将数组中源队列头的数据清掉(赋值为null)
public E pollFirst() {
int h = head;
@SuppressWarnings("unchecked")
E result = (E) elements[h];
// Element is null if deque empty
if (result == null)
return null;
elements[h] = null; // Must null out slot
head = (h + 1) & (elements.length - 1);
return result;
}
删除元素的示意图如下
3. 使用姿势&小结
- 若能确定队列的容量,在使用时请指定初始化容量,避免频繁的扩容
- 数组的实际容量必须为2的n次幂;初始化时传入一个非2的n次幂的容量参数时,会自动查找到刚好大于该参数的2的n次幂作为数组的实际长度
- 队列中不能有空元素
- 只有向队列中添加元素超过容量时,才会触发扩容逻辑(扩容为之前的两倍)
- 扩容后,数组中的实际顺序和队列顺序一致(即head会指向0,设计到数组的重排)
- head指向的是队列中第一个元素的下标位置;tail指向的是队列最后一个元素的后一位索引
- 队列头添加元素,是在head前一个数组位置处赋值;在队列尾添加元素是直接在tail指向的数组位置赋值
- 队列未发生扩容时,出队和进队都不会导致数组重排,只会改变head或tail的值而已
扫描关注,java分享
来源:oschina
链接:https://my.oschina.net/u/566591/blog/1554481