1.1 Deque源碼(基于JDK1.7.0_45)
本票中,我們來看看Deque源碼,在Queue基礎上,又增加了哪些功能?
Deque接口,是一個實現了雙端隊列數據結構的隊列,即在頭尾都可進行刪除和新增操作;
//接口Deuque:
public interface Deque<E> extends Queue<E> {
//將指定元素添加到雙端隊列的頭部(如果隊列滿了,則拋出異常)
void addFirst(E e);
//將指定元素添加到雙端隊列的頭部(如果隊列滿了,則返回fasle)
boolean offerFirst(E e);
//將指定元素添加到雙端隊列的尾部(如果隊列滿了,則拋出異常)
void addLast(E e);
//將指定元素添加到雙端隊列的尾部(如果隊列滿了,則返回fasle)
boolean offerLast(E e);
//獲取并刪除該雙端隊列的第一個元素(如果雙端隊列為空,則拋出異常)
E removeFirst();
//獲取并刪除雙端隊列的第一個元素(如果雙端隊列為空,則返回null)
E pollFirst();
//獲取并刪除該雙端隊列的最后一個元素(如果雙端隊列為空,則拋出異常)
E removeLast();
//獲取并刪除該雙端隊列的最后一個元素(如果雙端隊列為空,則返回null)
E pollLast();
//獲取但不刪除雙端隊列的第一個元素(如果雙端隊列為空,則拋出異常)
E getFirst();
//獲取但不刪除雙端隊列的第一個元素(如果雙端隊列為空,則返回null)
E peekFirst();
//獲取但不刪除雙端隊列的最后一個元素(如果雙端隊列為空,則拋出異常)
E getLast();
//獲取但不刪除雙端隊列的最后一個元素(如果雙端隊列為空,則返回null)
E peekLast();
//刪除該雙端隊列的第一次出現的元素o
boolean removeFirstOccurrence(Object o);
//刪除該雙端隊列的最后一次出現的元素o
boolean removeLastOccurrence(Object o);
//與Queue同理:
boolean add(E e);
//與Queue同理:
boolean offer(E e);
//與Queue同理:
E remove();
//與Queue同理:
E poll();
//與Queue同理:
E element();
//與Queue同理:
E peek();
//與Queue同理:
boolean remove(Object o);
模擬數據結構--棧-將元素壓入棧頂(向棧頂添加元素)
void push(E e);
模擬數據結構--棧-將元素移出棧頂(從棧頂刪除元素)
E pop();
boolean contains(Object o);
public int size();
Iterator<E> iterator();
Iterator<E> descendingIterator();
}
Deque(Double ended queue),雙端隊列的縮寫。一般隊列是能在隊頭獲取(刪除)元素,在隊尾添加元素,即只有一個進入端,一個輸出端。而在Deque中,實現了兩個進入端、兩個輸出端--即可在頭部輸出也可輸入,即可在尾部輸出也可在尾部輸入。
Deque繼承了Queue的所有方法,自然可以當做隊列來使用,依舊保持著“先進先出”的本質,在隊頭移除元素,在隊尾添加元素;
但是,Deque還可以被當做“棧”來使用,即“后進先出”,添加元素、刪除元素都在隊頭進行;主要通過push()、pop()兩個方法來實現;
通過上面的框架圖,可以清楚的看出,Deque接口主要有2個實現類,分別為ArrayDeque、LinkedList;
不過,還有一個并發實現類LinkedBlockingDeque,待我們下一篇幅在做介紹;
LinkedList不用多說,在之前List講解中已做過介紹,感興趣的朋友可以去學習下。
本篇主要講解ArrayDeque,通過名字可以看出,它底層數據結構由數組實現,既然是數組,想必也會實現自動擴容的機制!
1.2 ArrayDeque源碼(基于JDK1.7.0_45)
值得一提的是,Java中有一個類叫做Stack,該類實現了棧結構。但是,Java已經不提倡Stack來實現棧結構,而是建議使用ArrayDeque類;
來自Stack類描述:
A more complete and consistent set of LIFO stack operations is
provided by the {@link Deque} interface and its implementations, which
should be used in preference to this class. For example:
Deque<Integer> stack = new ArrayDeque<Integer>();}</pre>
@author Jonathan Payne
@since JDK1.0
在看源碼前,先給大家介紹下ArrayDeque到底如何添加元素、刪除元素的?這樣更有利于各位的理解!
在ArrayDeque中主要分為幾類方法:
添加:雙端隊列(頭尾都可以增加元素)
插入到隊列頭部:addFirst--offerFirst--將新增元素插入到數組的最大角標處,依次遞減;
插入到隊列尾部:addLast--offerLast--將新增元素插入到數組的0角標處,依次遞增;
刪除:
刪除隊列頭部第一個元素:removeFirst--pollFirst--將元素移除,移除此時head處的元素;
刪除隊列尾部最后一個元素:removeLast--pollLast--將元素移除,移除此時tail處的元素;
獲取:
獲取隊列頭部第一個元素:getFirst--peekFirst
獲取隊列尾部最后一個元素:getLast--peekLast
以上方法,在理解時候,切不可將隊列的頭尾理解成數組的頭、尾。隊列的頭尾值得是head、tail指針;
ArrayDeque主要源碼:(進行了刪減)
ArrayDeque成員變量和構造方法:
public class ArrayDeque<E> extends AbstractCollection<E>
implements Deque<E>, Cloneable, Serializable {
//底層數據結構實現---數組[]
private transient E[] elements;
//隊列頭指針:默認為0
private transient int head;
//隊列尾指針:默認為0
private transient int tail;
//最小初始化容量值:
private static final int MIN_INITIAL_CAPACITY = 8;
//擴大數組的長度:擴大為原有長度的2倍;
private void doubleCapacity() {
//assert斷言修飾符--只有當head==tail時候才會進行擴容操作;
//(前提是assert開啟,否則assert修飾符無效)
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)
throw new IllegalStateException("Sorry, deque too big");
Object[] a = new Object[newCapacity];
//連續調用2次的目的,是為了把原數組中所有的元素全部復制到新數組中(看圖說話)
System.arraycopy(elements, p, a, 0, r);
System.arraycopy(elements, 0, a, r, p);
elements = (E[])a;
head = 0;
tail = n;
}
//默認構造函數:隊列的長度默認為16
public ArrayDeque() {
elements = (E[]) new Object[16];
}
//可設置隊列大小的構造函數:
public ArrayDeque(int numElements) {
allocateElements(numElements);
}
//帶集合的構造函數:
public ArrayDeque(Collection<? extends E> c) {
allocateElements(c.size());
addAll(c);
}
}
ArrayDeque添加元素:
//向隊列的頭部插入元素:初始時從數組的最大角標處插入;
public void addFirst(E e) {
//插入元素不能為null:
if (e == null)
throw new NullPointerException();
//計算插入的角標: head = (head-1 與運算 數組長度-1)
elements[head = (head - 1) & (elements.length - 1)] = e;
//如果頭尾指針相同,則進行擴容操作:
if (head == tail)
doubleCapacity();
}
//向隊列的頭部插入元素:底層調用addFirst(E e)
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
//向隊列的末端插入元素:初始時從數組角標為0處插入;
public void addLast(E e) {
//插入元素不能為null:
if (e == null)
throw new NullPointerException();
//插入數組中,位置就是尾指針的值;
elements[tail] = e;
//判斷是否需要進行擴容操作:(tail+1 位運算 數組長度-1)是否與head的值相等
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
//擴容操作:
doubleCapacity();
}
//向隊列的末端插入元素:底層調用addLast(E e)
public boolean offerLast(E e) {
addLast(e);
return true;
}
ArrayDeque刪除元素:
//移除隊列中第一個元素:移除數組中head指針所指向的元素;
public E removeFirst() {
E x = pollFirst();
//如果隊列中沒有元素,則拋出異常
if (x == null)
throw new NoSuchElementException();
return x;
}
//移除隊列頭中的元素,實際就是移除數組中head指針所指向的元素;
public E pollFirst() {
//獲取頭指針:
int h = head;
//獲取頭指針所處的數組角標元素:
E result = elements[h]; // Element is null if deque empty
//如果頭指針為null,說明隊列中沒有元素存在,直接返回;
if (result == null)
return null;
//將頭指針所指向數組角標置為null:
elements[h] = null; // Must null out slot
//修改頭指針大小 = 頭指針+1 & 數組長度-1
//實際上是將現有頭指針+1。
head = (h + 1) & (elements.length - 1);
//返回被刪除的對象:
return result;
}
//移除隊列中最后一個元素:移除數組中tail指針所指向的元素;
public E removeLast() {
E x = pollLast();
//如果隊列中沒有元素,則拋出異常
if (x == null)
throw new NoSuchElementException();
return x;
}
//移除隊列尾中的元素,實際就是移除數組中tail指針所指向的元素;
public E pollLast() {
//獲取要移除元素的數組角標:如果tail為0,則計算出的t值為數組的最大角標(首尾相連了)
int t = (tail - 1) & (elements.length - 1);
//獲取要移除的數組元素:
E result = elements[t];
//如果為null,則直接返回;
if (result == null)
return null;
//將對應數組角標的元素置為null:
elements[t] = null;
//修改tail指針的值:
tail = t;
return result;
}
ArrayDeque獲取元素:
//得到隊列中的第一個元素,也就是頭指針所指向的元素;
public E getFirst() {
E x = elements[head];
//為空的話,拋出異常;
if (x == null)
throw new NoSuchElementException();
return x;
}
//得到隊列中的第一個元素,隊列空的話返回null
public E peekFirst() {
return elements[head]; // elements[head] is null if deque empty
}
//得到隊列中的最后一個元素,也就是尾指針所指向的元素;
public E getLast() {
E x = elements[(tail - 1) & (elements.length - 1)];
if (x == null)
throw new NoSuchElementException();
return x;
}
//得到隊列中的最后一個元素,隊列空的話返回null
public E peekLast() {
return elements[(tail - 1) & (elements.length - 1)];
}
ArrayDeque實現隊列的方法:
//隊列方法:
//向隊列中添加元素:添加到隊列頭部
public boolean add(E e) {
addLast(e);
return true;
}
//向隊列中添加元素:添加到隊列末尾
public boolean offer(E e) {
return offerLast(e);
}
//移除隊列中元素,移除隊列頭部元素,為null拋出異常;
public E remove() {
return removeFirst();
}
//移除隊列中元素,移除隊列頭部元素,可以為null;
public E poll() {
return pollFirst();
}
//獲取隊列頭部的元素:如果為null拋出異常;
public E element() {
return getFirst();
}
//獲取隊列頭部的元素:如果為null,就返回;
public E peek() {
return peekFirst();
}
ArrayDeque實現棧的方法:
//棧方法:
//向棧頂壓入對象:向數組最大角標處插入對象;
public void push(E e) {
addFirst(e);
}
//將棧頂對象出棧,移除head指針所屬的元素;
public E pop() {
return removeFirst();
}
此外,ArrayDeque利用了push/pop方法,實現了棧結構,棧是一種“后進先出”的數據模型。
在ArrayDeque中,底層主要利用addFirst/removeFirst實現元素的出棧和入棧操作;
1.3 LinkedList源碼(基于JDK1.7.0_45)
LinkedList即有List集合方法,又有Deque集合方法。
具體解析,請參考筆者之前的文章。