java容器類4:Queue深刻解讀

1594931-293dba23ebc504c2

Collection的其它兩大分支:List和Set在前面已近分析過,這篇來分析一下Queue的底層實現。html

前三篇關於Java容器類的文章:java

java容器類1:Collection,List,ArrayList,LinkedList深刻解讀

java容器類2:Map及HashMap深刻解讀

java容器類3:set/HastSet/MapSet深刻解讀

Queue


public interface Queue<E> extends Collection<E> {
    boolean add(E var1);

    boolean offer(E var1);

    E remove();

    E poll();

    E element();

    E peek();
}
這就是Queue接口的代碼,相比於List或者Set簡潔明瞭不少。下面介紹一下它裏面接口的含義:
在處理元素前用於保存元素的 collection。除了基本的  操做外,隊列還提供其餘的Collection

插入、提取和檢查c++

操做。每一個方法都存在兩種形式:

一種拋出異常(操做失敗時),另外一種返回一個特殊值(nullfalse,具體取決於操做)。web

插入操做的後一種形式是用於專門爲有容量限制的 Queue 實現設計的;在大多數實現中,插入操做不會失敗。
image
 
 
 
1182892-20171122100317930-842768608

AbstractQueue


AbstractQueue中實現了queue和Collection中部分函數,比較簡單,源碼以下:api

public abstract class AbstractQueue<E> extends AbstractCollection<E>
    implements Queue<E> {
    protected AbstractQueue() {
    }
    public boolean add(E e) {
        if (offer(e))
            return true;
        else
            throw new IllegalStateException("Queue full");
    }


    public E remove() {
        E x = poll();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

    public E element() {
        E x = peek();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

    public void clear() {
        while (poll() != null)
            ;
    }

    public boolean addAll(Collection<? extends E> c) {
        if (c == null)
            throw new NullPointerException();
        if (c == this)
            throw new IllegalArgumentException();
        boolean modified = false;
        for (E e : c)
            if (add(e))
                modified = true;
        return modified;
    }
}

從上面的調用關係能夠看出來,Queue的解釋中哪些是會拋出異常的調用,哪些是不會拋出異常的調用接口。數組

Deque


一個線性 collection,支持在兩端插入和移除元素。名稱 deque 是「double ended queue(雙端隊列)」的縮寫,一般讀爲「deck」。大多數 Deque 實現對於它們可以包含的元素數沒有固定限制,但此接口既支持有容量限制的雙端隊列,也支持沒有固定大小限制的雙端隊列。(java 1.6版本中的家扣,1.8中接口有變更,可是大概含義類似)數據結構

image

Java容器類1中介紹了LinkedList,鏈表類其實實現了 Deque的接口,因此鏈表支持從頭部和尾部添加和移除元素。多線程

ArrayDeque


由於鏈表的存儲結構可能比較簡單,這裏介紹一下ArrayDeque,它的裏面存儲元素使用一個數組。 做爲一個雙端隊列的數組,涉及到擴容和元素的拷貝的邏輯可能比較複雜些。ide

看一下里面的幾個構造函數:函數

public ArrayDeque() {
        elements = 
new Object[16];
    }


public ArrayDeque(int numElements) {
        
allocateElements(numElements);
    }

public ArrayDeque(Collection<? extends E> c) {
        allocateElements(c.size());
        addAll(c);
    }

從構造函數能夠看出默認的會分配一個長度爲16的數組。同時,也支持指定大小的隊列(這裏的allocateElements函數以前在

java容器類2:Map及HashMap深刻解讀  中已經深刻分析過,是個很是精妙的函數)。下面看一下究竟是如何實現插入?又是如何自動擴充數組的?

ArrayQueue中維護了兩個成員變量:head和tail分別表明 隊列的頭和尾在數組中的下標。

 /**
     * The index of the element at the head of the deque (which is the
     * element that would be removed by remove() or pop()); or an
     * arbitrary number equal to tail if the deque is empty.
     */
    transient int head;

    /**
     * The index at which the next element would be added to the tail
     * of the deque (via addLast(E), add(E), or push(E)).
     */
    transient int tail;

在隊列的首部添加元素:

public void addFirst(E e) {
        if (e == null)
            throw new NullPointerException();
        
elements[head = (head - 1) & (elements.length - 1)] = e;
        if (head == tail)
            
doubleCapacity();
    }
public void
addLast(E e) {
if
 (e == 
null
)
throw new
 NullPointerException();
elements[tail] = e;
if
 ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
}

由構造函數和數組分配的函數能夠知道,數組的長度確定是一個2的冪次方的一個整數。

當head爲大於0的整數時,在頭部插入很簡單,將head前一個元素賦值爲e就能夠了。那麼當head爲0時,怎麼計算的?由上面能夠看出會插入到數組的尾部。因此ArrayDeque至關於在一個圓環上,規定一個頭一個尾做爲隊列的先後(將數組的首位相連)。

在最後位置添加元素的原理和在首部添加類似。注意判斷是否已滿的 判斷,這裏再也不分析。

當隊列已經滿後,會將數組的長度double。因爲數組是不能自由擴張的,因此doubleCapacity函數應該是分配一個更大的數組,並將原來的元素拷貝進去,這裏再也不分析。

總的來講雙端隊列ArrayDeque是在數組的基礎之上實現,原理和實現都不算複雜,可是不少邊界調節等細節能夠斟酌。

BlockingQueue


BlockingQueue是concurrent包下面的,後續打算寫一個系列文章專門分析concurrent包下面的類,及一些多線程相關的東西。

PriorityQueue


優先級隊列是一個能夠排序的隊列。內部是一個最大堆,大部分人應該瞭解堆排序,因此對最大堆應該不會陌生。

每次讀取元素都是讀取最大的元素(默認狀況下)。

對外的接口有以下:

方法名 功能描述
add(E e) 添加元素
clear() 清空
contains(Object o) 檢查是否包含當前參數元素
offer(E e) 添加元素
peek() 讀取元素,(不刪除)
poll() 取出元素,(刪除)
remove(Object o) 刪除指定元素
size() 返回長度

PriorityQueue 默認是一個最大堆結構,若是想構造一個最小堆:

private static final int DEFAULT_INITIAL_CAPACITY = 11;
PriorityQueue<Integer> maxHeap=new PriorityQueue<Integer>(DEFAULT_INITIAL_CAPACITY, new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2-o1;
        }
    });

關於堆的數據結構部分這裏再也不分析能夠參考:http://www.javashuo.com/article/p-ydzmfqux-dq.html

c++版的優先級隊列分析:優先級隊列用法詳解(priority_queue)

因爲是經過數組保存數據,因此優先級隊列也會涉及到容量的擴充等,和HashMap/Setting/Collection的擴容原理相同,甚至更簡單,再也不分析。PriorityQueue內部的操做都是在最大堆的基礎上展開的,閱讀堆的數據結構相關資料即可瞭解。

參考:

http://tool.oschina.net/uploads/apidocs/jdk-zh/java/util/Deque.html

http://www.cnblogs.com/NeilZhang/p/5650226.html

相關文章
相關標籤/搜索