Collection的其它兩大分支:List和Set在前面已近分析過,這篇來分析一下Queue的底層實現。html
前三篇關於Java容器類的文章:java
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++
操做。每一個方法都存在兩種形式:
一種拋出異常(操做失敗時),另外一種返回一個特殊值(null 或 false,具體取決於操做)。web
插入操做的後一種形式是用於專門爲有容量限制的 Queue 實現設計的;在大多數實現中,插入操做不會失敗。
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的解釋中哪些是會拋出異常的調用,哪些是不會拋出異常的調用接口。數組
一個線性 collection,支持在兩端插入和移除元素。名稱 deque 是「double ended queue(雙端隊列)」的縮寫,一般讀爲「deck」。大多數 Deque 實現對於它們可以包含的元素數沒有固定限制,但此接口既支持有容量限制的雙端隊列,也支持沒有固定大小限制的雙端隊列。(java 1.6版本中的家扣,1.8中接口有變更,可是大概含義類似)數據結構
在Java容器類1中介紹了LinkedList,鏈表類其實實現了 Deque的接口,因此鏈表支持從頭部和尾部添加和移除元素。多線程
由於鏈表的存儲結構可能比較簡單,這裏介紹一下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();if
elements[tail] = e;
( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
}
由構造函數和數組分配的函數能夠知道,數組的長度確定是一個2的冪次方的一個整數。
當head爲大於0的整數時,在頭部插入很簡單,將head前一個元素賦值爲e就能夠了。那麼當head爲0時,怎麼計算的?由上面能夠看出會插入到數組的尾部。因此ArrayDeque至關於在一個圓環上,規定一個頭一個尾做爲隊列的先後(將數組的首位相連)。
在最後位置添加元素的原理和在首部添加類似。注意判斷是否已滿的 判斷,這裏再也不分析。
當隊列已經滿後,會將數組的長度double。因爲數組是不能自由擴張的,因此doubleCapacity函數應該是分配一個更大的數組,並將原來的元素拷貝進去,這裏再也不分析。
總的來講雙端隊列ArrayDeque是在數組的基礎之上實現,原理和實現都不算複雜,可是不少邊界調節等細節能夠斟酌。
BlockingQueue是concurrent包下面的,後續打算寫一個系列文章專門分析concurrent包下面的類,及一些多線程相關的東西。
優先級隊列是一個能夠排序的隊列。內部是一個最大堆,大部分人應該瞭解堆排序,因此對最大堆應該不會陌生。
每次讀取元素都是讀取最大的元素(默認狀況下)。
對外的接口有以下:
方法名 | 功能描述 |
---|---|
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