ArrayDeque是一个双向队列,既可以实现栈,也能用作队列。内部通过数组来实现,能够同时在两端进行插入删除的操作,同时也是非线程安全的,如果在多线程中使用它,需要自己去处理同步操作,它不允许放入null元素。用作栈时,优于Stack,用作队列时,效率高于LinkedList.
构造函数:
public ArrayDeque() {
//默认初始化一个长度为16的数组
elements = new Object[16];
}
传参数时则根据参数来初始化数组,或者也可以传入一个Collection用于构造队列。
看看ArrayDeque的基本方法和源码:
从尾部添加元素:
* boolean add(E e): return true,throws NullPointerException
* void addLast(E e):throws NullPointerException,等同add()
* boolean offer(E e): return true,throws NullPointerException
* boolean offerLast(E e):return true, throws NullPointerException
上面的几个方法其实内部都是调用了addLast(),本质是一样的。只是addLast没有返回值。
#看看addLast()的源码:
public void addLast(E e) {
//不允许插入空元素
if (e == null)
throw new NullPointerException();
//tail是尾部元素下一个null元素的index,直接将e赋给这个索引的元素。
elements[tail] = e;
//插入之后tail往后移动一位, tail =(tail + 1) & (elements.length -1) :因为elements是2的倍数,所以elements.length -1 低位全是1.那么(tail + 1) & (elements.length -1)在前者比后者小或等于后者时,结果等于tail +1.但是,当tail +1 超过(elements.length -1)时,即tail +1 = elements.length时,再& ,结果为0,即tail ==0,tail回到初始值。
/若tail=head说明数组满了。进行扩容。
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
}
//让ArrayDeque的容量增大一倍。当ArrayDeque容量满了的时候,即head == tail时
private void doubleCapacity() {
assert head == tail;
int p = head;
int n = elements.length;
int r = n - p; //head右边的长度
int newCapacity = n << 1; //容量*2
if (newCapacity < 0) //超出了int限制
throw new IllegalStateException("Sorry, deque too big");
Object[] a = new Object[newCapacity];
/**
* @param src 要进行复制的数组
* @param srcPos 开始复制的位置
* @param dest 目标数组
* @param destPos 目标数组开始复制的位置
* @param length 源数组要复制的长度
* arraycopy(Object src, int srcPos, Object dest, int destPos,int length);
*/
System.arraycopy(elements, p, a, 0, r);
System.arraycopy(elements, 0, a, r, p);
//首先将head右边的元素复制到新的数组中,然后将0到head的元素复制追加到新数组后面。
elements = a;
head = 0;
tail = n; //因为 n = 旧的数组长度,即是新的数组下一个可以插入元素的位置
}
从头部添加元素:
* void addFirst(E e ): throws NullPointerException
* boolean offerFirst(E e): return true, throws NullPointerException
* void push(E e): 等同于addFirst()
addFirst()[push()] 和offerFirst()唯一不同之处在于offerFirst返回值为true,而另外两个为void。
看看addFirst(E e)的源码:
public void addFirst(E e) {
if (e == null)
throw new NullPointerException();
//head ==0 时,head -1 =-1,负数在计算机中以补码表示,此时(head - 1)& (elements.length - 1)结果其实就是(elements.length - 1)。
//可以看到当head =0 时,从头添加其实就是从数组最大位置开始添加,这也证实了数组是循环数组。当head > 0时,从head的左边往回添加。把这个数组想象成一个圆环。
elements[head = (head - 1) & (elements.length - 1)] = e;
//如果head和tail相等,即说明下一个插入的位置就在head了,即是数组已经循环了一圈,所以进行扩容。
if (head == tail)
doubleCapacity();
}
从头部移除元素:
* E poll() :等同于pollFirst()
* E pollFirst():
* E pop() : throws NoSuchElementException
* E remove() : throws NoSuchElementException,等于removeFirst()
* E removeFirst() :throws NoSuchElementException
1、2等价,3、4、5等价,1、2和3、4、5之间的区别也在于是否抛出异常。其实内部都是通过pollFirst()来移除。
public E pollFirst() {
final Object[] elements = this.elements;
final int h = head;
@SuppressWarnings("unchecked")
E result = (E) elements[h];
//直接定位到head索引位置,将该位置置空,此时head要后移一位,因为原本该位置已经为null.
if (result != null) {
elements[h] = null;
//保证数组下标不越界
head = (h + 1) & (elements.length - 1);
}
return result;
}
从尾部移除元素:
* E pollLast():
* E removeLast(): throws NoSuchElementException
//将tail -1 索引位置的元素删除即可。tail往回移一位。
public E pollLast() {
final Object[] elements = this.elements;
final int t = (tail - 1) & (elements.length - 1);
@SuppressWarnings("unchecked")
E result = (E) elements[t];
if (result != null) {
elements[t] = null;
tail = t;
}
return result;
}
移除指定元素以及指定顺序的元素:
* boolean remove(Object o) ,等价于removeFirstOccurrence(Object o)
* boolean removeFirstOccurrence(Object o):从头往尾部检索,将第一次出现的object对象移除
* boolean removeLastOccurrence(Object o) : 从头部往尾部检索,将最后出现的该object对象移除
获取指定位置元素,直接通过head和tail索引来获取头部或者尾部元素即可。
* E element():获取队列的头部元素,等价于getFirst(), throws NoSuchElementException
* getFirst(): 获取头部元素,throws NoSuchElementException
* getLast():获取尾部元素,throws NoSuchElementException
* peek():获取头部元素,等价于peekFirst()
* peekFirst()
* peekLast():获取尾部元素
获取ArrayDeque的迭代器:
* Iterator<E> descendingIterator():返回逆序的队列迭代器,从tail开始往回读取数据
* Iterator<E> iterator():返回正常顺序的迭代器,从head开始往后读取数据