Java集合--Queue(Java中實現2)

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集合方法。

具體解析,請參考筆者之前的文章。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Queue Queue繼承自 Collection,我們先來看看類結構吧,代碼量比較少,我直接貼代碼了 從方法名上...
    Anonymous___閱讀 892評論 0 1
  • 今天我們來介紹下集合Queue中的幾個重要的實現類。關于集合Queue中的內容就比較少了。主要是針對隊列這種數據結...
    Ruheng閱讀 8,621評論 4 27
  • 棧 棧的英文單詞是Stack,它代表一種特殊的線性表,這種線性表只能在固定一端(通常認為是線性表的尾端)進行插入,...
    Jack921閱讀 1,521評論 0 5
  • 以下是《瘋狂Java講義》中的一些知識,如有錯誤,煩請指正。 集合概述 Java集合可以分為Set、List、Ma...
    hainingwyx閱讀 554評論 0 1
  • 很多事,可以借力。比如: 1.留言入選,可以在貓群里求贊,可以在朋友圈求贊。 2.以前會覺得讓別人點贊會覺得不好意...
    映月黑珍珠閱讀 163評論 0 1