public class ArrayDeque<E> extends AbstractCollection<E>
implements Deque<E>, Cloneable, Serializable
transient Object[] elements;
transient int head;
transient int tail;
-------、
public interface Deque<E> extends Queue<E> {
從定義可以看出,ArrayDeque類,實現了 Deque接口,而Deque繼承了Queue(隊列)接口;
而Queue接口,有以下功能:
boolean add(E e)
boolean offer(E e)
E remove();
E poll();
E element();
E peek();
Deque接口,定義了以下方法:
void addFirst(E e);
void addLast(E e);
boolean offerFirst(E e);
boolean offerLast(E e);
E removeFirst();
E removeLast();
E pollFirst();
E pollLast();
E getFirst();
E getLast();
E peekFirst();
E peekLast();
---------
boolean removeFirstOccurrence(Object o);
boolean removeLastOccurrence(Object o)
boolean add(E e);
boolean offer(E e);
E remove();
E poll();
E element();
E peek();
-----------
boolean addAll(Collection<? extends E> c);
void push(E e);
E pop();
-----------
boolean remove(Object o);
boolean contains(Object o);
int size();
Iterator<E> iterator();
Iterator<E> descendingIterator();
從源碼來看,ArrayDeque采用了類似與雙指針的雙端隊列結構,
其中Deque可以分為Queue 和 Stack相對應的接口;
上面兩個表共定義了Deque的12個接口。添加,刪除,取值都有兩套接口,它們功能相同,區別是對失敗情況的處理不同。一套接口遇到失敗就會拋出異常,另一套遇到失敗會返回特殊值(false或null)。除非某種實現對容量有限制,大多數情況下,添加操作是不會失敗的。雖然Deque的接口有12個之多,但無非就是對容器的兩端進行操作,或添加,或刪除,或查看。明白了這一點講解起來就會非常簡單。
ArrayDeque和LinkedList是Deque的兩個通用實現,由于官方更推薦使用AarryDeque用作棧和隊列,加之上一篇已經講解過LinkedList,本文將著重講解ArrayDeque的具體實現。
從名字可以看出ArrayDeque底層通過數組實現,為了滿足可以同時在數組兩端插入或刪除元素的需求,該數組還必須是循環的,即循環數組(circular array),也就是說數組的任何一點都可能被看作起點或者終點。ArrayDeque是非線程安全的(not thread-safe),當多個線程同時使用的時候,需要程序員手動同步;另外,該容器不允許放入null元素。
摘抄自大神
由于底層為 circular array,故其 head 和 tail 在數值上大小不一定,head代表頭部的第一個有效元素,tail代表尾部可以插入的位置;
其示意圖如下:
方法剖析
1、addLast()方法
像隊尾增加元素(tail),
public void addLast(E e) {
if (e == null) //隊列中不允許放入null
throw new NullPointerException();
final Object[] es = elements; //新建了一個新的數組,來引用原數組
es[tail] = e; // 先插入,在判斷是否進行擴容
if (head == (tail = inc(tail, es.length))) //若列表已滿,則進行擴容
grow(1);
}
static final int inc(int i, int modulus) {
if (++i >= modulus) i = 0;
//判斷隊尾是否已經在數組的尾部,若在則tail下一步為0
return i;
}
此時,tail的下一個空位為 tail+1
此時,tail的下一個空位為 0
2、addFirst() 方法
此時情況與addLast相似,只需江 tail+1 改為head-1 即可;
public void addFirst(E e) {
if (e == null)
throw new NullPointerException();
final Object[] es = elements;
es[head = dec(head, es.length)] = e; // 先插入,在判斷是否進行擴容
if (head == tail)
grow(1);
}
static final int dec(int i, int modulus) {
if (--i < 0) i = modulus - 1;
return i;
}
上述代碼我們看到,空間問題是在插入之后解決的,因為tail總是指向下一個可插入的空位,也就意味著elements數組至少有一個空位,所以插入元素的時候不用考慮空間問題。
擴容的方法:
private void grow(int needed) {
// overflow-conscious code
final int oldCapacity = elements.length;
int newCapacity;
// Double capacity if small; else grow by 50%
int jump = (oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1);
// 判斷原數組長度
if (jump < needed
|| (newCapacity = (oldCapacity + jump)) - MAX_ARRAY_SIZE > 0)
newCapacity = newCapacity(needed, jump);
final Object[] es = elements = Arrays.copyOf(elements, newCapacity);
// Exceptionally, here tail == head needs to be disambiguated
if (tail < head || (tail == head && es[head] != null)) {
// wrap around; slide first leg forward to end of array
int newSpace = newCapacity - oldCapacity;
System.arraycopy(es, head,
es, head + newSpace,
oldCapacity - head);
for (int i = head, to = (head += newSpace); i < to; i++)
es[i] = null;
}
}
private int newCapacity(int needed, int jump) {
final int oldCapacity = elements.length, minCapacity;
if ((minCapacity = oldCapacity + needed) - MAX_ARRAY_SIZE > 0) {
if (minCapacity < 0)
throw new IllegalStateException("Sorry, deque too big");
return Integer.MAX_VALUE;
}
if (needed > jump)
return minCapacity;
return (oldCapacity + jump - MAX_ARRAY_SIZE < 0)
? oldCapacity + jump
: MAX_ARRAY_SIZE;
}
擴容分區間進行擴容,在不超過最大限度情況下,若源列表的長度小于64,則擴容為2倍,否則為1.5倍;
擴容完后,要將原有元素復制到新的隊列中去;
3、pollFirst() 方法
public E pollFirst() {
final Object[] es;
final int h;
E e = elementAt(es = elements, h = head);
if (e != null) {
es[h] = null;
head = inc(h, es.length);
}
return e;
}
static final <E> E elementAt(Object[] es, int i) {
return (E) es[i];
}
pollFirst()的作用是刪除并返回Deque首端元素,也即是head位置處的元素。如果容器不空,只需要直接返回elements[head]即可,當然還需要處理下標的問題。由于ArrayDeque中不允許放入null,當elements[head] == null時,意味著容器為空。
4、 pollLast()方法
public E pollLast() {
final Object[] es;
final int t;
E e = elementAt(es = elements, t = dec(tail, es.length));
if (e != null)
es[tail = t] = null;
return e;
}
pollLast()的作用是刪除并返回Deque尾端元素,也即是tail位置前面的那個元素。
5、peekFirst() 和peekLast()
只返回,首端元素,并不刪除元素;