ArrayDeque類的使用詳解


ArrayDequeDeque接口的一個實現,使用了可變數組,所以沒有容量上的限制。
同時, ArrayDeque是線程不安全的,在沒有外部同步的情況下,不能再多線程環境下使用。
ArrayDequeDeque的實現類,可以作為棧來使用,效率高於 Stack
也可以作為隊列來使用,效率高於 LinkedList
需要注意的是, ArrayDeque不支持 null值。
 

一、常用方法

1.添加元素
        addFirst(E e)在數組前面添加元素
        addLast(E e)在數組后面添加元素
        offerFirst(E e) 在數組前面添加元素,並返回是否添加成功
        offerLast(E e) 在數組后天添加元素,並返回是否添加成功

  2.刪除元素
        removeFirst()刪除第一個元素,並返回刪除元素的值,如果元素為null,將拋出異常
        pollFirst()刪除第一個元素,並返回刪除元素的值,如果元素為null,將返回null
        removeLast()刪除最后一個元素,並返回刪除元素的值,如果為null,將拋出異常
        pollLast()刪除最后一個元素,並返回刪除元素的值,如果為null,將返回null
        removeFirstOccurrence(Object o) 刪除第一次出現的指定元素
        removeLastOccurrence(Object o) 刪除最后一次出現的指定元素
   

   3.獲取元素
        getFirst() 獲取第一個元素,如果沒有將拋出異常
        getLast() 獲取最后一個元素,如果沒有將拋出異常
   

    4.隊列操作
        add(E e) 在隊列尾部添加一個元素
        offer(E e) 在隊列尾部添加一個元素,並返回是否成功
        remove() 刪除隊列中第一個元素,並返回該元素的值,如果元素為null,將拋出異常(其實底層調用的是removeFirst())
        poll()  刪除隊列中第一個元素,並返回該元素的值,如果元素為null,將返回null(其實調用的是pollFirst())
        element() 獲取第一個元素,如果沒有將拋出異常
        peek() 獲取第一個元素,如果返回null
      

    5.棧操作
        push(E e) 棧頂添加一個元素
        pop(E e) 移除棧頂元素,如果棧頂沒有元素將拋出異常
        

    6.其他
        size() 獲取隊列中元素個數
        isEmpty() 判斷隊列是否為空
        iterator() 迭代器,從前向后迭代
        descendingIterator() 迭代器,從后向前迭代
        contain(Object o) 判斷隊列中是否存在該元素
        toArray() 轉成數組
        clear() 清空隊列
        clone() 克隆(復制)一個新的隊列

二、源碼分析

結構

ArrayDeque底層使用了數組來存儲數據,同時用兩個intheadtail來表示頭部和尾部。

不過需要注意的是tail並不是尾部元素的索引,而是尾部元素的下一位,即下一個將要被加入的元素的索引。

//用數組存儲元素
transient Object[] elements; // non-private to simplify nested class access
//頭部元素的索引
transient int head;
//尾部下一個將要被加入的元素的索引
transient int tail;
//最小容量,必須為2的冪次方
private static final int MIN_INITIAL_CAPACITY = 8;

2. 構造方法 

ArrayDeque有三個構造函數來初始化,除了無參的構造函數使用了默認容量,其它兩個構造函數會通過allocateElements來計算初始容量。

public ArrayDeque() {  
    elements = (E[]) new Object[16]; // 默認的數組長度大小  
}  
  
public ArrayDeque(int numElements) {  
    allocateElements(numElements); // 需要的數組長度大小  
}  
  
public ArrayDeque(Collection<? extends E> c) {  
    allocateElements(c.size()); // 根據集合來分配數組大小  
    addAll(c); // 把集合中元素放到數組中  
}  

3. 分配合適大小的數組 

ArrayDeque 對數組的大小(即隊列的容量)有特殊的要求,必須是 2^n。我們來看一下allocateElements方法。

private void allocateElements(int numElements) {  
    int initialCapacity = MIN_INITIAL_CAPACITY;  
    // 找到大於需要長度的最小的2的冪整數。  
    // 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 = (E[]) new Object[initialCapacity];  
}  
這里的實現很有意思,對於一個小於 2^30的值,經過五次右移和位或操作后,可以得到一個 2^k - 1的值。最后再將這個值 +1,得到 2^k。通過這個方法,可以將一個任意的初始值轉化為 2^n的值,不過有一點不足在於,如果本身傳進來的值就是 2^n的值,那么經過轉化會變成 2^(n+1),所以我們在不用刻意去傳入 2^n的值。還有一點在於,如果傳入的值大於等於 2^30,那么經過轉化會變成負值,即 < 0,此時會把初始值設置為 2^30,即最大的容量只有 2^30

4. 擴容 

// 擴容為原來的2倍。  
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)  
        throw new IllegalStateException("Sorry, deque too big");  
    Object[] a new Object[newCapacity];  
    // 既然是head和tail已經重合了,說明tail是在head的左邊。  
    System.arraycopy(elements, p, a, 0, r); // 拷貝原數組從head位置到結束的數據  
    System.arraycopy(elements, 0, a, r, p); // 拷貝原數組從開始到head的數據  
    elements = (E[])a;  
    head = 0; // 重置head和tail為數據的開始和結束索引  
    tail = n;  
}  
  
// 拷貝該數組的所有元素到目標數組  
private <T> T[] copyElements(T[] a) {  
    if (head < tail) { // 開始索引大於結束索引,一次拷貝  
        System.arraycopy(elements, head, a, 0, size());  
    } else if (head > tail) { // 開始索引在結束索引的右邊,分兩段拷貝  
        int headPortionLen = elements.length - head;  
        System.arraycopy(elements, head, a, 0, headPortionLen);  
        System.arraycopy(elements, 0, a, headPortionLen, tail);  
    }  
    return a;  
}  

5. 添加元素 

public void addFirst(E e) {  
    if (e == null)  
        throw new NullPointerException();  
    // 本來可以簡單地寫成head-1,但如果head為0,減1就變為-1了,和elements.length - 1進行與操作就是為了處理這種情況,這時結果為elements.length - 1。  
    elements[head = (head - 1) & (elements.length - 1)] = e;  
    if (head == tail) // head和tail不可以重疊  
        doubleCapacity();  
}  
  
public void addLast(E e) {  
    if (e == null)  
        throw new NullPointerException();  
    // tail位置是空的,把元素放到這。  
    elements[tail] = e;  
    // 和head的操作類似,為了處理臨界情況 (tail為length - 1時),和length - 1進行與操作,結果為0。  
    if ( (tail = (tail + 1) & (elements.length - 1)) == head)  
        doubleCapacity();  
}  
  
public boolean offerFirst(E e) {  
    addFirst(e);  
    return true;  
}  
  
public boolean offerLast(E e) {  
    addLast(e);  
    return true;  
}  

6. 刪除元素 刪除首尾元素:

public E removeFirst() {  
    E x = pollFirst();  
    if (x == null)  
        throw new NoSuchElementException();  
    return x;  
}  
  
public E removeLast() {  
    E x = pollLast();  
    if (x == null)  
        throw new NoSuchElementException();  
    return x;  
}  
  
public E pollFirst() {  
    int h = head;  
    E result = elements[h]; // Element is null if deque empty  
    if (result == null)  
        return null;  
    // 表明head位置已為空  
    elements[h] = null;     // Must null out slot  
    head = (h + 1) & (elements.length - 1); // 處理臨界情況(當h為elements.length - 1時),與后的結果為0。  
    return result;  
}  
  
public E pollLast() {  
    int t = (tail - 1) & (elements.length - 1); // 處理臨界情況(當tail為0時),與后的結果為elements.length - 1。  
    E result = elements[t];  
    if (result == null)  
        return null;  
    elements[t] null;  
    tail = t; // tail指向的是下個要添加元素的索引。  
    return result;  
}  

刪除指定元素

public boolean removeFirstOccurrence(Object o) {  
    if (o == null)  
        return false;  
    int mask = elements.length - 1;  
    int i = head;  
    E x;  
    while ( (x = elements[i]) != null) {  
        if (o.equals(x)) {  
            delete(i);  
            return true;  
        }  
        i = (i + 1) & mask; // 從頭到尾遍歷  
    }  
    return false;  
}  
  
public boolean removeLastOccurrence(Object o) {  
    if (o == null)  
        return false;  
    int mask = elements.length - 1;  
    int i = (tail - 1) & mask; // 末尾元素的索引  
    E x;  
    while ( (x = elements[i]) != null) {  
        if (o.equals(x)) {  
            delete(i);  
            return true;  
        }  
        i = (i - 1) & mask; // 從尾到頭遍歷  
    }  
    return false;  
}  

private void checkInvariants() { // 有效性檢查  
    assert elements[tail] == null; // tail位置沒有元素  
    assert head == tail ? elements[head] == null :  
        (elements[head] != null &&  
            elements[(tail - 1) & (elements.length - 1)] != null); // 如果head和tail重疊,隊列為空;否則head位置有元素,tail-1位置有元素。  
    assert elements[(head - 1) & (elements.length - 1)] == null; // head-1的位置沒有元素。  
}  
  
private boolean delete(int i) {  
    checkInvariants();  
    final E[] elements = this.elements;  
    final int mask = elements.length - 1;  
    final int h = head;  
    final int t = tail;  
    final int front = (i - h) & mask; // i前面的元素個數  
    final int back  = (t - i) & mask; // i后面的元素個數  
  
    // Invariant: head <= i < tail mod circularity  
    if (front >= ((t - h) & mask)) // i不在head和tail之間  
        throw new ConcurrentModificationException();  
  
    // Optimize for least element motion  
    if (front < back) { // i的位置靠近head,移動開始的元素,返回false。  
        if (h <= i) {  
            System.arraycopy(elements, h, elements, h + 1, front);  
        } else { // Wrap around  
            System.arraycopy(elements, 0, elements, 1, i);  
            elements[0] = elements[mask]; // 處理邊緣元素  
            System.arraycopy(elements, h, elements, h + 1, mask - h);  
        }  
        elements[h] = null;  
        head = (h + 1) & mask; // head位置后移  
        return false;  
    } else { // i的位置靠近tail,移動末尾的元素,返回true。  
        if (i < t) { // Copy the null tail as well  
            System.arraycopy(elements, i + 1, elements, i, back);  
            tail = t - 1;  
        } else { // Wrap around  
            System.arraycopy(elements, i + 1, elements, i, mask - i);  
            elements[mask] = elements[0];  
            System.arraycopy(elements, 1, elements, 0, t);  
            tail = (t - 1) & mask;  
        }  
        return true;  
    }  
}  

7. 獲取元素

public E getFirst() {  
    E x = elements[head];  
    if (x == null)  
        throw new NoSuchElementException();  
    return x;  
}  
  
public E getLast() {  
    E x = elements[(tail - 1) & (elements.length - 1)]; // 處理臨界情況(當tail為0時),與后的結果為elements.length - 1。  
    if (x == null)  
        throw new NoSuchElementException();  
    return x;  
}  
  
public E peekFirst() {  
    return elements[head]; // elements[head] is null if deque empty  
}  
  
public E peekLast() {  
    return elements[(tail - 1) & (elements.length - 1)];  
}  

8. 隊列操作 

public boolean add(E e) {  
    addLast(e);  
    return true;  
}  
  
public boolean offer(E e) {  
    return offerLast(e);  
}  
  
public E remove() {  
    return removeFirst();  
}  
  
public E poll() {  
    return pollFirst();  
}  
  
public E element() {  
    return getFirst();  
}  
  
public E peek() {  
    return peekFirst();  
}  

9. 棧操作 

public void push(E e) {  
    addFirst(e);  
}  
  
public E pop() {  
    return removeFirst();  
}  

10. 集合方法 

public int size() {  
    return (tail - head) & (elements.length - 1); // 和elements.length - 1進行與操作是為了處理當tail < head時的情況。  
}  
  
public boolean isEmpty() {  
    return head == tail; // tail位置的元素一定為空,head和tail相等,也為空。  
}  
  
// 向前迭代器  
public Iterator<E> iterator() {  
    return new DeqIterator();  
}  
  
// 向后迭代器  
public Iterator<E> descendingIterator() {  
    return new DescendingIterator();  
}  

  private class DeqIterator implements Iterator<E> {  
  
      private int cursor = head;  
  
      private int fence = tail; // 迭代終止索引,同時也為了檢測並發修改。  
  
      private int lastRet = -1; // 最近的next()調用返回的索引。據此可以定位到需要刪除元素的位置。  
  
      public boolean hasNext() {  
          return cursor != fence;  
      }  
  
      public E next() {  
          if (cursor == fence)  
              throw new NoSuchElementException();  
          E result = elements[cursor];  
          // This check doesn't catch all possible comodifications,  
          // but does catch the ones that corrupt traversal  
          if (tail != fence || result == null)  
              throw new ConcurrentModificationException();  
          lastRet = cursor;  
          cursor = (cursor + 1) & (elements.length - 1); // 游標位置加1  
          return result;  
      }  
  
      public void remove() {  
          if (lastRet < 0)  
              throw new IllegalStateException();  
          if (delete(lastRet)) { // 如果將元素從右往左移,需要將游標減1。  
              cursor = (cursor - 1) & (elements.length - 1); // 游標位置回退1。  
fence = tail; // 重置閥值。  
   }  
          lastRet = -1;  
      }  
  }  

private class DescendingIterator implements Iterator<E> {  
  
    private int cursor = tail; // 游標開始索引為tail  
    private int fence = head; // 游標的閥值為head  
    private int lastRet = -1;  
  
    public boolean hasNext() {  
        return cursor != fence;  
    }  
  
    public E next() {  
        if (cursor == fence)  
            throw new NoSuchElementException();  
        cursor = (cursor - 1) & (elements.length - 1); // tail是下個添加元素的位置,所以要減1才是尾節點的索引。  
        E result = elements[cursor];  
        if (head != fence || result == null)  
            throw new ConcurrentModificationException();  
        lastRet = cursor;  
        return result;  
    }  
  
    public void remove() {  
        if (lastRet < 0)  
            throw new IllegalStateException();  
        if (!delete(lastRet)) { // 如果從左往右移,需要將游標加1。  
            cursor = (cursor + 1) & (elements.length - 1);  
            fence = head;  
        }  
        lastRet = -1;  
    }  
}  
  
public boolean contains(Object o) {  
    if (o == null)  
        return false// ArrayDeque不可以存儲null元素  
    int mask = elements.length - 1;  
    int i = head;  
    E x;  
    while ( (x = elements[i]) != null) {  
        if (o.equals(x))  
            return true;  
        i = (i + 1) & mask; // 處理臨界情況  
    }  
    return false;  
}  
  
public boolean remove(Object o) {  
    return removeFirstOccurrence(o);  
}  
  
public void clear() {  
    int h = head;  
    int t = tail;  
    if (h != t) { // clear all cells  
        head = tail = 0; // 重置首尾索引  
        int i = h;  
        int mask = elements.length - 1;  
        do {  
            elements[i] null// 清除元素  
            i = (i + 1) & mask;  
        } while (i != t);  
    }  
}  
  
public Object[] toArray() {  
    return copyElements(new Object[size()]); // 把所有元素拷貝到新創建的Object數組上,所以對返回數組的修改不會影響該雙端隊列。  
}  
  
public <T> T[] toArray(T[] a) {  
    int size = size();  
    if (a.length < size) // 目標數組大小不夠  
        a = (T[])java.lang.reflect.Array.newInstance(  
                a.getClass().getComponentType(), size); // 利用反射創建類型為T,大小為size的數組。  
yElements(a); // 拷貝所有元素到目標數組。  
    if (a.length > size)  
        a[size] null// 結束標識  
    return a;  
}  

11. Object方法 

public ArrayDeque<E> clone() {  
    try {  
        ArrayDeque<E> result = (ArrayDeque<E>) super.clone();  
        result.elements = Arrays.copyOf(elements, elements.length); // 深度復制。  
        return result;  
  
    } catch (CloneNotSupportedException e) {  
        throw new AssertionError();  
    }  
}  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM