Deque
概述
一個線性 collection,支持在兩端插入和移除元素。名稱 deque 是“double ended queue(雙端隊列)”的縮寫,通常讀為“deck”。大多數
Deque
實現對於它們能夠包含的元素數沒有固定限制,但此接口既支持有容量限制的雙端隊列,也支持沒有固定大小限制的雙端隊列。
特點
Deque
是一個Queue的子接口,是一個雙端隊列,支持在兩端插入和移除元素deque
支持索引值直接存取。Deque
頭部和尾部添加或移除元素都非常快速。但是在中部安插元素或移除元素比較費時。- 插入、刪除、獲取操作支持兩種形式:快速失敗和返回null或true/false
- 不推薦插入null元素,null作為特定返回值表示隊列為空
常用方法
第一個元素(頭部) | 最后一個元素(尾部) | |||
---|---|---|---|---|
拋出異常 | 特殊值 | 拋出異常 | 特殊值 | |
插入 | addFirst(e) |
offerFirst(e) |
addLast(e) |
offerLast(e) |
移除 | removeFirst() |
pollFirst() |
removeLast() |
pollLast() |
檢查 | getFirst() |
peekFirst() |
getLast() |
peekLast() |
雙向隊列操作
插入元素
- addFirst(): 向隊頭插入元素,如果元素為null,則發生空指針異常
- addLast(): 向隊尾插入元素,如果為空,則發生空指針異常
- offerFirst(): 向隊頭插入元素,如果插入成功返回true,否則返回false
- offerLast(): 向隊尾插入元素,如果插入成功返回true,否則返回false
移除元素
- removeFirst(): 返回並移除隊頭元素,如果該元素是null,則發生NoSuchElementException
- removeLast(): 返回並移除隊尾元素,如果該元素是null,則發生NoSuchElementException
- pollFirst(): 返回並移除隊頭元素,如果隊列無元素,則返回null
- pollLast(): 返回並移除隊尾元素,如果隊列無元素,則返回null
獲取元素
- getFirst(): 獲取隊頭元素但不移除,如果隊列無元素,則發生NoSuchElementException
- getLast(): 獲取隊尾元素但不移除,如果隊列無元素,則發生NoSuchElementException
- peekFirst(): 獲取隊頭元素但不移除,如果隊列無元素,則返回null
- peekLast(): 獲取隊尾元素但不移除,如果隊列無元素,則返回null
棧操作
pop(): 彈出棧中元素,也就是返回並移除隊頭元素,等價於removeFirst()
,如果隊列無元素,則發生NoSuchElementException
push(): 向棧中壓入元素,也就是向隊頭增加元素,等價於addFirst()
,如果元素為null,則發生NoSuchElementException,如果棧空間受到限制,則發生IllegalStateException
引用場景
- 滿足FIFO場景時
- 滿足LIFO場景時,曾經在解析XML按標簽時使用過棧這種數據結構,但是卻選擇
Stack
類,如果在進行棧選型時,更推薦使用Deque
類,應為Stack
是線程同步
ArrayDeque
概述
Deque
接口的大小可變數組的實現。數組雙端隊列沒有容量限制;它們可根據需要增加以支持使用。它們不是線程安全的;在沒有外部同步時,它們不支持多個線程的並發訪問。禁止 null 元素。此類很可能在用作堆棧時快於Stack
,在用作隊列時快於LinkedList
。
特點
- 初始容量為16,每次擴容都會翻倍,並且容量一定是2^n。
public ArrayDeque() {
elements = new Object[16];
}
public ArrayDeque(int numElements) {
allocateElements(numElements);
}
private void allocateElements(int numElements) {
int initialCapacity = MIN_INITIAL_CAPACITY;
// Find the best power of two to hold elements.
// Tests "<=" because arrays aren't kept full.
if (numElements >= initialCapacity) {
initialCapacity = numElements;
initialCapacity |= (initialCapacity >>> 1);
initialCapacity |= (initialCapacity >>> 2);
initialCapacity |= (initialCapacity >>> 4);
initialCapacity |= (initialCapacity >>> 8);
initialCapacity |= (initialCapacity >>> 16);
initialCapacity++;
if (initialCapacity < 0) // Too many elements, must back off
initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
}
elements = new Object[initialCapacity];
}
>>>
是無符號右移操作,|
是位或操作,經過五次右移和位或操作可以保證得到大小為 2^n-1 的數。最后在自增一,就是 2^n。
- 增加或刪除(head)
public void addFirst(E e) {
if (e == null)
throw new NullPointerException();
elements[head = (head - 1) & (elements.length - 1)] = e;//注意點
if (head == tail)
doubleCapacity();
}
elements[head = (head - 1) & (elements.length - 1)] = e;
當head為0時,實際上是11111111&00001111,結果是00001111,也就是物理數組的尾部15;當head增長如head+1超過物理數組長度如16時,實際上是00010000&00001111,結果00000000,也就是0,這樣就回到了物理數組的頭部.
相當於head初始值0時,第一次就將head指針定位到數組末尾了,然后指針從后向前移動。
而addLast就與之相反,控制tail指針,從前向后移動。當tail和head相遇了就說明空間已經滿了。(就像一個圓環結構)
以上
@Fzxey