瞭解特性,先看下體系結構:
如上所看到的,知道其支持 序列化,克隆,迭代器操做,隊列特性。詳細實現 除了實現以上接口外,擴展AbstractCollection 抽象類。html
大炮打蒼蠅。仍是鳥槍打野豬?工具應用要有場景:
ArrayDeque 爲雙端隊列,支持首部,尾部兩端的操做,所以作雙端操做可用於fifo等queue, 作單端操做可作爲stack.
然而能作queue的還有linkedList, vector等,能作stack有Stack,還要ArrayDeque幹嗎?
仍是那句話,「應用」是要講究「場景」的:
linkedList內部實現用node節點連接先後元素。模擬c/c++的鏈表(長處在於中間節點的增刪操做爲o(1))。
vector方法加着synchronized修飾(同步 將帶來性能的損耗)。Stack的實現又繼承自vector,問題同上。
ArrayDeque 的底層實現爲單純的數組操做。因此單從性能上看。ArrayDeque在優於他們。固然由於沒有作同步處理,因此存在併發問題。需要調用方本身保障。java
源代碼實現部分。看上去仍是相對親切的(當年大學書本上背爛的鏈表,堆,隊列實現),又複習了一遍C語言入門的隊列實現。node
固然jdk中的設計畢竟是巧妙的。
挑幾處分析下:linux
public boolean isEmpty() {
return head == tail;
}
public int size() {
return (tail - head) & (elements.length - 1);
}
public E pollFirst() {
int h = head;
E result = 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;
}
public E pollLast() {
int t = (tail - 1) & (elements.length - 1);
E result = elements[t];
if (result == null)
return null;
elements[t] = null;
tail = t;
return result;
}
先說明下關鍵變量 意義:
head 第一個元素的索引
tail 最後一個元素以後的索引。
如 head指向0。tail指向3。那麼有效元素爲索引0,1,2 指向的元素,因此元素個數 size() 爲 3(即3-0)。c++
看下上述實現奇怪的地方,大量如此的操做,爲什麼?算法
(tail - head) & (elements.length - 1)
(h + 1) & (elements.length - 1)
緣由在於 arraydeque在實現雙端隊列時才用爲循環數組。存在 tail < head的狀況,因此要經過 & (elements.length - 1)操做來修正,使其爲正值。數組
因此咱們會想到經過補碼是實現是可以的,然而單憑 &(elements.length - 1) 就實現修正,仍是難以讓咱們信服的。markdown
若是 tail = 3, head = 6, elements.length=10, 那麼結果也應該是 7啊 (元素的索引爲6,7。8,9。0,1,2),而非 -3&10 = 8 啊 (-3 補碼 1111 1101 10補碼 0000 1010 結果 0000 1000 爲8)
不思不得其解,再看源代碼,當中有一句話
/**
* The minimum capacity that we’ll use for a newly created deque.
* Must be a power of 2.
*/
因此,一切都明確了,elements.length 即數組長度是有要求的,必須是2的冪指數,如此一切可解。測試一下elments.length 爲16,32等都順利成章經過了。原理也很是easy,elments.length 二進制位 00100..00,elments.length-1 爲 000111…11可以作掩碼了。如此,上述源代碼理解經過。併發
關於arrayDequeue在分配空間,要求必須是 2的冪指數。當中有一段實現:工具
private void allocateElements(int numElements) {
int initialCapacity = MIN_INITIAL_CAPACITY;
// Find the best power of two to hold elements.
// 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];
}
關於此處算法的出處。我的眼下尚未理解,哪位大俠知道還請告知。但是可否work,咱們仍是要驗證一下,一下借用一下他人的方法:
public class ArrayDequeCapacity {
public static void main(String[] args) {
for (int i = 1; i < 32; i++) {
int n = (int) Math.pow(2, i) - 1;
System.out.println(i + " " + n + " " + getCapacity(n));
}
}
private static int getCapacity(int numElements) {
int 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
return initialCapacity;
}
}
結果:
1 1 2 2 3 4 3 7 8 4 15 16 5 31 32 6 63 64 7 127 128 8 255 256 9 511 512 10 1023 1024 11 2047 2048 12 4095 4096 13 8191 8192 14 16383 16384 15 32767 32768 16 65535 65536 17 131071 131072 18 262143 262144 19 524287 524288 20 1048575 1048576 21 2097151 2097152 22 4194303 4194304 23 8388607 8388608 24 16777215 16777216 25 33554431 33554432 26 67108863 67108864 27 134217727 134217728 28 268435455 268435456 29 536870911 536870912 30 1073741823 1073741824 31 2147483646 1073741824
從驗證結果來看,對於計算大於等於一個數的最小2的冪指數運算,這種方法仍是工做的很是好的。
那麼。問題又來了。爲何分配空間必定要按2的冪指數。我是贊同其它同窗觀點的(有其它理由的還請補充):
linux內存分配管理中的夥伴系統以連續2的冪指數(1。2,4,8,…1024)個頁幀(page frame) 爲單位進行空間分配,而頁幀作爲標準內存分配單元大小4K, 因此才用 2的冪指數爲大數組分配空間是有利於進行內存管理的。
如上式:
public int size() {
return (tail - head) & (elements.length - 1);
}
若是沒有對 elements.length作要求的話,就要例如如下推斷來
int sub = tail - head;
if(sub >= 0) return sub;
else {
return sub + elements.length;
}
關於arrayDequeue的應用和實現。大體如此。