一、类继承关系
Deque接口说明和使用参考上一篇java8 LinkedList接口实现源码解析
二、接口实现
ArrayDeque是基于数组的Deque接口实现类,内存存储结构简单,当需要FIFO队列,LIFO队列或者Stack时,官方推荐优先使用ArrayDeque。线程不安全,会自动扩容,遍历时修改会快速失败。
1、全局变量和公共方法
/**
* 保存元素的数组,数组的长度总是2的整数次方,数组不允许填满,同时保证没有保存元素的数组位都是null
*/
transient Object[] elements; // non-private to simplify nested class access
/**
* 头元素的对应的数组下标,从最大值开始递减,未插入时head为0
*/
transient int head;
/**
* 下一个尾元素对应的数组下标,从0开始递增,当等于head时表示数组已满
*/
transient int tail;
/**
* 数组的最低容量
*/
private static final int MIN_INITIAL_CAPACITY = 8;
private static int calculateSize(int numElements) {
int initialCapacity = MIN_INITIAL_CAPACITY;
//计算大于numElements的最小的2的整数次方,同HashMap
if (numElements >= initialCapacity) {
initialCapacity = numElements;
initialCapacity |= (initialCapacity >>> 1);
initialCapacity |= (initialCapacity >>> 2);
initialCapacity |= (initialCapacity >>> 4);
initialCapacity |= (initialCapacity >>> 8);
initialCapacity |= (initialCapacity >>> 16);
initialCapacity++;
//超过了int类型的最大值变成负数了
if (initialCapacity < 0) // Too many elements, must back off
initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
}
return initialCapacity;
}
/**
* 初始化数组
*/
private void allocateElements(int numElements) {
elements = new Object[calculateSize(numElements)];
}
//数组扩容
private void doubleCapacity() {
//head等于tail即表明数组已经填满了
assert head == tail;
int p = head;
int n = elements.length;
int r = n - p; // 即数组下标为head的右边的元素的个数,即通过addFirst添加的元素的个数
//扩容一倍
int newCapacity = n << 1;
//超过int的最大值变成负值
if (newCapacity < 0)
throw new IllegalStateException("Sorry, deque too big");
Object[] a = new Object[newCapacity];
//将通过addFirst添加的元素和通过addLast添加的元素整体交换位置,head变成0,tail变成n,
// 这样下一次插入元素就可以直接插入到扩容的那部分数组里面,同时保证了pollLast()和pollFirst()时元素的顺序仍然
//是之前插入的顺序
System.arraycopy(elements, p, a, 0, r);
System.arraycopy(elements, 0, a, r, p);
elements = a;
head = 0;
tail = n;
}
//将列表中元素复制到一个新数组中
private <T> T[] copyElements(T[] a) {
//假定数组a足够大,大于size()
if (head < tail) {//只有当head为0即没有调用addFirst时head才会小于tail
System.arraycopy(elements, head, a, 0, size());
} else if (head > tail) {//数组的头部和尾部都有元素
int headPortionLen = elements.length - head;//头部元素的个数
//复制头部元素到新数组的前面
System.arraycopy(elements, head, a, 0, headPortionLen);
//复制尾部元素到新数组的后面,从而保证对新数组遍历时返回元素的顺序依然是元素添加的顺序
System.arraycopy(elements, 0, a, headPortionLen, tail);
}
return a;
}
2、构造方法
public ArrayDeque() {
elements = new Object[16];
}//默认数组长度是16
public ArrayDeque(int numElements) {
allocateElements(numElements);
}
public ArrayDeque(Collection<? extends E> c) {
allocateElements(c.size());
addAll(c);
}
3、元素插入
public void addFirst(E e) {
if (e == null)
throw new NullPointerException();
//head的初始值为0,每次addFirst都会减一,
//以数组长度16为例,head为0时head变成15,下一次调用head变成14,一直到head等于tail执行扩容,head又变成0
elements[head = (head - 1) & (elements.length - 1)] = e;
if (head == tail)
doubleCapacity();
}
//等价于add()
public void addLast(E e) {
if (e == null)
throw new NullPointerException();
//以数组长度16为例,tail的初始值为0,然后是1,2,一直到head等于tail
elements[tail] = e;
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
}
3、元素删除
public E pollFirst() {
int h = head;
@SuppressWarnings("unchecked")
E result = (E) elements[h];//取出head对应的元素
if (result == null)//队列为空,不需要重置head
return null;
elements[h] = null;
//重置head,addFirst是减一,此处就加1
head = (h + 1) & (elements.length - 1);
return result;
}
public E pollLast() {
int t = (tail - 1) & (elements.length - 1);//取出上一次添加的元素,addLast时加1了,此处就减一
@SuppressWarnings("unchecked")
E result = (E) elements[t];
if (result == null) //队列为空,无需重置tail
return null;
elements[t] = null;
tail = t;//重置tail
return result;
}
public boolean removeFirstOccurrence(Object o) {
if (o == null)
return false;
int mask = elements.length - 1;
int i = head;
Object x;
//按照元素添加addFirst的顺序遍历查找
while ( (x = elements[i]) != null) {
if (o.equals(x)) {
delete(i);
return true;
}
i = (i + 1) & mask;
}
return false;
}
public boolean removeLastOccurrence(Object o) {
if (o == null)
return false;
int mask = elements.length - 1;
int i = (tail - 1) & mask;
Object x;
//按照元素添加addLast的顺序遍历查找
while ( (x = elements[i]) != null) {
if (o.equals(x)) {
delete(i);
return true;
}
i = (i - 1) & mask;
}
return false;
}
//该方法删除元素会导致元素往数组的前后移动,数组下标为0的那一端为后,向前移动时tail不变,head减1,返回false,想后移动时tail减1,head不变,返回true
//假定i位于数组的索引范围内
private boolean delete(int i) {
checkInvariants();
final Object[] elements = this.elements;
final int mask = elements.length - 1;
final int h = head;
final int t = tail;
//(i - h) & mask表示在双向链表中目标索引i前面的元素个数,以数组长度8为例,t为2,h为5,当i为5时,计算结果为0,当i为1时,计算结果为4
final int front = (i - h) & mask;
//(t - i) & mask表示在双向链表中目标索引i后面的元素个数,以数组长度8为例,t为2,h为5,当i为5时,计算结果为5,当i为1时,计算结果为1
final int back = (t - i) & mask;
//(t - h) & mask表示数组中已经插入的元素的个数,以数组长度8为例,t为2,h为5,算出来的结果为5
//综上此表达式成立表示tail<=i<head,此时i对应的数组位为null
if (front >= ((t - h) & mask))
throw new ConcurrentModificationException();
//判断目标索引位于双向链表中的前半部分还是后半部分,从而决定移动的方向,保证需要移动的元素的个数最少
if (front < back) {//位于双向链表中前半部分,将目标元素之前的元素往后移动即可,双向链表中往后在数组中是往前
if (h <= i) {
//被删除元素位于头部,将该元素之后插入的front个元素往前挪一位
System.arraycopy(elements, h, elements, h + 1, front);
} else {
//被删除元素位于尾部,将目标索引i之前的元素往前挪一位,把第一次掉addFirst插入的元素放到下标为0的位置
System.arraycopy(elements, 0, elements, 1, i);
elements[0] = elements[mask];
//将头部的元素整体往后移一位
System.arraycopy(elements, h, elements, h + 1, mask - h);
}
//h对应的数组位置null,重置head
elements[h] = null;
head = (h + 1) & mask;
return false;
} else {
if (i < t) {
//被删除元素位于尾部,将目标索引i之后的back个元素往后移动一位
System.arraycopy(elements, i + 1, elements, i, back);
//重置tail
tail = t - 1;
} else {
//被删除元素位于头部,将目标索引i之后的mask-i个元素往后移动一位,用数组下标为0的元素填充数组下标最大的元素
System.arraycopy(elements, i + 1, elements, i, mask - i);
elements[mask] = elements[0];
//尾部元素整体往后移动一位
System.arraycopy(elements, 1, elements, 0, t);
tail = (t - 1) & mask;
}
return true;
}
}
4、元素查找
public boolean contains(Object o) {
if (o == null)
return false;
int mask = elements.length - 1;
int i = head;
Object x;
//从头部往后遍历查找
while ( (x = elements[i]) != null) {
if (o.equals(x))
return true;
i = (i + 1) & mask;
}
return false;
}
5、元素遍历
private class DeqIterator implements Iterator<E> {
//正序遍历从头元素开始
private int cursor = head;
//元素遍历的索引边界
private int fence = tail;
//上一个返回的元素的索引
private int lastRet = -1;
public boolean hasNext() {
return cursor != fence;
}
public E next() {
if (cursor == fence)
throw new NoSuchElementException();
@SuppressWarnings("unchecked")
E result = (E) elements[cursor];
if (tail != fence || result == null)
throw new ConcurrentModificationException();
lastRet = cursor;
//同pollFirst()方法
cursor = (cursor + 1) & (elements.length - 1);
return result;
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
//delete方法返回true时,tail减一,head不变,这时元素往数组下标为0的方向移动了一位,
//即往与遍历方向相反的方向移动了一位,为了保证下一个元素依然不变,需要将cursor减1,
//tail减一了,fence需要重置
//delete方法返回false时,tail不变,head减一,这时是之前遍历过的元素向着遍历方向移动了一位
//所以cursor不变
if (delete(lastRet)) {
cursor = (cursor - 1) & (elements.length - 1);
fence = tail;
}
lastRet = -1;
}
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
Object[] a = elements;
int m = a.length - 1, f = fence, i = cursor;
cursor = f;
while (i != f) {
@SuppressWarnings("unchecked") E e = (E)a[i];
i = (i + 1) & m;
if (e == null)
throw new ConcurrentModificationException();
action.accept(e);
}
}
}
private class DescendingIterator implements Iterator<E> {
/*
* 倒序遍历从尾部开始
*/
private int cursor = tail;
//元素遍历的索引边界
private int fence = head;
private int lastRet = -1;
public boolean hasNext() {
return cursor != fence;
}
public E next() {
if (cursor == fence)
throw new NoSuchElementException();
//同pollLast()的逻辑
cursor = (cursor - 1) & (elements.length - 1);
@SuppressWarnings("unchecked")
E result = (E) elements[cursor];
if (head != fence || result == null)
throw new ConcurrentModificationException();
lastRet = cursor;
return result;
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
//delete方法返回false时,head会加1,需要重置head,tail不变,元素向着与遍历元素相反的方向(即往数组下标最大的元素)移动,
//为保证遍历的元素不变需要对cursor加1
//delete方法返回true是,head不变,tail-1,元素向着与遍历元素相同的方向(即往数组下标为0的方向移动)移动,且移动的是之前遍历过的元素
//所以cursor维持不变
if (!delete(lastRet)) {
cursor = (cursor + 1) & (elements.length - 1);
fence = head;
}
lastRet = -1;
}
}