JDK容器學習之Queue: ArrayDeque

數組雙端隊列 ArrayDeque

雙端隊列,表示能夠添加元素到(或刪除,獲取)隊列頭也能夠添加元素到(或刪除,獲取)隊列尾java

title

1. 底層數據結構

類中定義成員變量,一個數組和兩個int數組

transient Object[] elements;

transient int head;

transient int tail;

數據結構比較清晰,就是一個數組,head指向隊列的頭,tail指向隊列的尾數據結構

數組定義要求數組的容量爲2的n次冪設計

2. 常見接口實現方式

刪除元素

先看刪除邏輯,由於比較簡單,實現以下code

public E poll() {
    if (size == 0) // 隊列爲空
        return null;
    int s = --size;
    modCount++;
    // 數組中第一個爲隊列頭
    E result = (E) queue[0]; 
    E x = (E) queue[s];
    queue[s] = null;
    if (s != 0) 
        // 隊列非空時,重排剩下的元素
        siftDown(0, x);
    return result;
}

添加元素

在隊頭和隊尾添加的邏輯基本一致,這裏以在隊列尾添加元素績進行分析對象

public void addLast(E e) {
    if (e == null) // 不支持向隊列中塞入null對象
        throw new NullPointerException();
    elements[tail] = e;
    if ( (tail = (tail + 1) & (elements.length - 1)) == head) {
    // tail 後移一位,若tail長度超過數組長度,則tail轉到數組頭
    // 上面的與操做等價於對數組長度進行求餘
    // 若tail和head相等,則表示數組內容填充滿,須要擴容
        doubleCapacity();
    }
}

// 數組擴容方法
private void doubleCapacity() {
    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) // int逸出判斷
        throw new IllegalStateException("Sorry, deque too big");
    Object[] a = new Object[newCapacity];
    System.arraycopy(elements, p, a, 0, r);
    System.arraycopy(elements, 0, a, r, p);
    elements = a;
    head = 0;
    tail = n;
}

下面是一個數組擴容的邏輯示意圖索引

  • 圖中上面的爲原數組,添加元素8以後,tail和head相等,都是5
  • 原隊列容量爲8,順序爲 1 -> 9 -> 5 -> 7 -> 4 -> 3 -> 5 -> 8
  • 擴容後,數組容量爲16,順序保持不變,head爲0,tail爲8

add

彈出元素

以彈出隊頭的元素爲例,會直接返回隊列頭的元素,並將head前移一位,並將數組中源隊列頭的數據清掉(賦值爲null)接口

public E pollFirst() {
    int h = head;
    @SuppressWarnings("unchecked")
    E result = (E) elements[h];
    // Element is null if deque empty
    if (result == null)
        return null;
    elements[h] = null;     // Must null out slot
    head = (h + 1) & (elements.length - 1);
    return result;
}

刪除元素的示意圖以下隊列

remove

3. 使用姿式&小結

  1. 若能肯定隊列的容量,在使用時請指定初始化容量,避免頻繁的擴容
  2. 數組的實際容量必須爲2的n次冪;初始化時傳入一個非2的n次冪的容量參數時,會自動查找到恰好大於該參數的2的n次冪做爲數組的實際長度
  3. 隊列中不能有空元素
  4. 只有向隊列中添加元素超過容量時,纔會觸發擴容邏輯(擴容爲以前的兩倍)
  5. 擴容後,數組中的實際順序和隊列順序一致(即head會指向0,設計到數組的重排)
  6. head指向的是隊列中第一個元素的下標位置;tail指向的是隊列最後一個元素的後一位索引
  7. 隊列頭添加元素,是在head前一個數組位置處賦值;在隊列尾添加元素是直接在tail指向的數組位置賦值
  8. 隊列未發生擴容時,出隊和進隊都不會致使數組重排,只會改變head或tail的值而已

掃描關注,java分享

相關文章
相關標籤/搜索