1 public class ArrayDeque<E> extends AbstractCollection<E> 2 implements Deque<E>, Cloneable, Serializable
1 public interface Queue<E> extends Collection<E> { 2 // 增長一個元素到隊尾,若是隊列已滿,則拋出一個IIIegaISlabEepeplian異常 3 boolean add(E e); 4 // 添加一個元素到隊尾並返回true,若是隊列已滿,則返回false 5 boolean offer(E e); 6 // 移除並返回隊列頭部的元素,若是隊列爲空,則拋出一個NoSuchElementException異常 7 E remove(); 8 // 移除並返問隊列頭部的元素,若是隊列爲空,則返回null 9 E poll(); 10 // 返回隊列頭部的元素,若是隊列爲空,則拋出一個NoSuchElementException異常 11 E element(); 12 // 返問隊列頭部的元素,若是隊列爲空,則返回null 13 E peek(); 14 }
1 // 底層用數組存儲元素 2 private transient E[] elements; 3 // 隊列的頭部元素索引(即將pop出的一個) 4 private transient int head; 5 // 隊列下一個要添加的元素索引 6 private transient int tail; 7 // 最小的初始化容量大小,須要爲2的n次冪 8 private static final int MIN_INITIAL_CAPACITY = 8;
1 /** 2 * 默認構造方法,數組的初始容量爲16 3 */ 4 public ArrayDeque() { 5 elements = (E[]) new Object[16]; 6 } 7 8 /** 9 * 使用一個指定的初始容量構造一個ArrayDeque 10 */ 11 public ArrayDeque( int numElements) { 12 allocateElements(numElements); 13 } 14 15 /** 16 * 構造一個指定Collection集合參數的ArrayDeque 17 */ 18 public ArrayDeque(Collection<? extends E> c) { 19 allocateElements(c.size()); 20 addAll(c); 21 } 22 23 /** 24 * 分配合適容量大小的數組,確保初始容量是大於指定numElements的最小的2的n次冪 25 */ 26 private void allocateElements(int numElements) { 27 int initialCapacity = MIN_INITIAL_CAPACITY; 28 // 找到大於指定容量的最小的2的n次冪 29 // Find the best power of two to hold elements. 30 // Tests "<=" because arrays aren't kept full. 31 // 若是指定的容量小於初始容量8,則執行一下if中的邏輯操做 32 if (numElements >= initialCapacity) { 33 initialCapacity = numElements; 34 initialCapacity |= (initialCapacity >>> 1); 35 initialCapacity |= (initialCapacity >>> 2); 36 initialCapacity |= (initialCapacity >>> 4); 37 initialCapacity |= (initialCapacity >>> 8); 38 initialCapacity |= (initialCapacity >>> 16); 39 initialCapacity++; 40 41 if (initialCapacity < 0) // Too many elements, must back off 42 initialCapacity >>>= 1; // Good luck allocating 2 ^ 30 elements 43 } 44 elements = (E[]) new Object[initialCapacity]; 45 }
2^1 = 10 2^2 = 100 2^3 = 1000 2^n = 1(n個0)
再來看下2^n-1的二進制是什麼樣子的:html
2^1 - 1 = 01 2^2 - 1 = 011 2^3 - 1 = 0111 2^n - 1 = 0(n個1)
1 // 確保容量爲2的n次冪,是capacity爲大於initialCapacity的最小的2的n次冪 2 int capacity = 1; 3 while (capacity < initialCapacity) 4 capacity <<= 1;
那麼這兩種方法有什麼區別呢?HashMap中的這種寫法更容量理解,而ArrayDeque中的效果更高(最多通過4次位移和或操做+1次加一操做)。java
4.入隊(添加元素到隊尾)api
1 /** 2 * 增長一個元素,若是隊列已滿,則拋出一個IIIegaISlabEepeplian異常 3 */ 4 public boolean add(E e) { 5 // 調用addLast方法,將元素添加到隊尾 6 addLast(e); 7 return true; 8 } 9 10 /** 11 * 添加一個元素 12 */ 13 public boolean offer(E e) { 14 // 調用offerLast方法,將元素添加到隊尾 15 return offerLast(e); 16 } 17 18 /** 19 * 在隊尾添加一個元素 20 */ 21 public boolean offerLast(E e) { 22 // 調用addLast方法,將元素添加到隊尾 23 addLast(e); 24 return true; 25 } 26 27 /** 28 * 將元素添加到隊尾 29 */ 30 public void addLast(E e) { 31 // 若是元素爲null,咋拋出空指針異常 32 if (e == null) 33 throw new NullPointerException(); 34 // 將元素e放到數組的tail位置 35 elements[tail ] = e; 36 // 判斷tail和head是否相等,若是相等則對數組進行擴容 37 if ( (tail = (tail + 1) & ( elements.length - 1)) == head) 38 // 進行兩倍擴容 39 doubleCapacity(); 40 }
這裏,( (tail = (tail + 1) & ( elements.length - 1)) == head)這句代碼是關鍵,爲何會這樣寫呢。正常的添加元素後應該是將tail+1對不對,可是隊列的刪除和添加是不在同一端的,什麼意思呢,咱們畫個圖看一下。數組
2^1 = 10 2^2 = 100 2^3 = 1000 2^n = 1(n個0)
再來看下2^n-1的二進制是什麼樣子的:安全
2^1 - 1 = 01 2^2 - 1 = 011 2^3 - 1 = 0111 2^n - 1 = 0(n個1)
1 /** 2 * 數組將要滿了的時候(tail==head)將,數組進行2倍擴容 3 */ 4 private void doubleCapacity() { 5 // 驗證head和tail是否相等 6 assert head == tail; 7 int p = head ; 8 // 記錄數組的長度 9 int n = elements .length; 10 // 計算head後面的元素個數,這裏沒有采用jdk中自帶的英文註釋right,是由於所謂隊列的上下左右,只是咱們看的方位不一樣而已,若是上面畫的圖,這裏就應該是left而非right 11 int r = n - p; // number of elements to the right of p 12 // 將數組長度擴大2倍 13 int newCapacity = n << 1; 14 // 若是此時長度小於0,則拋出IllegalStateException異常,何時newCapacity會小於0呢,前面咱們說過了int值<<1越界 15 if (newCapacity < 0) 16 throw new IllegalStateException( "Sorry, deque too big" ); 17 // 建立一個長度是原數組大小2倍的新數組 18 Object[] a = new Object[newCapacity]; 19 // 將原數組head後的元素都拷貝值新數組 20 System. arraycopy(elements, p, a, 0, r); 21 // 將原數組head前的元素都拷貝到新數組 22 System. arraycopy(elements, 0, a, r, p); 23 // 將新數組賦值給elements 24 elements = (E[])a; 25 // 重置head爲數組的第一個位置索引0 26 head = 0; 27 // 重置tail爲數組的最後一個位置索引+1((length - 1) + 1) 28 tail = n; 29 }
1 /** 2 * 移除並返回隊列頭部的元素,若是隊列爲空,則拋出一個NoSuchElementException異常 3 */ 4 public E remove() { 5 // 調用removeFirst方法,移除隊頭的元素 6 return removeFirst(); 7 } 8 9 /** 10 * @throws NoSuchElementException {@inheritDoc} 11 */ 12 public E removeFirst() { 13 // 調用pollFirst方法,移除並返回隊頭的元素 14 E x = pollFirst(); 15 // 若是隊列爲空,則拋出NoSuchElementException異常 16 if (x == null) 17 throw new NoSuchElementException(); 18 return x; 19 } 20 21 /** 22 * 移除並返問隊列頭部的元素,若是隊列爲空,則返回null 23 */ 24 public E poll() { 25 // 調用pollFirst方法,移除並返回隊頭的元素 26 return pollFirst(); 27 } 28 29 public E pollFirst() { 30 int h = head ; 31 // 取出數組隊頭位置的元素 32 E result = elements[h]; // Element is null if deque empty 33 // 若是數組隊頭位置沒有元素,則返回null值 34 if (result == null) 35 return null; 36 // 將數組隊頭位置置空,也就是刪除元素 37 elements[h] = null; // Must null out slot 38 // 將head指針往前移動一個位置 39 head = (h + 1) & (elements .length - 1); 40 // 將隊頭元素返回 41 return result; 42 }
1 /** 2 * 返回隊列頭部的元素,若是隊列爲空,則拋出一個NoSuchElementException異常 3 */ 4 public E element() { 5 // 調用getFirst方法,獲取隊頭的元素 6 return getFirst(); 7 } 8 9 /** 10 * @throws NoSuchElementException {@inheritDoc} 11 */ 12 public E getFirst() { 13 // 取得數組head位置的元素 14 E x = elements[head ]; 15 // 若是數組head位置的元素爲null,則拋出異常 16 if (x == null) 17 throw new NoSuchElementException(); 18 return x; 19 } 20 21 /** 22 * 返回隊列頭部的元素,若是隊列爲空,則返回null 23 */ 24 public E peek() { 25 // 調用peekFirst方法,獲取隊頭的元素 26 return peekFirst(); 27 } 28 29 public E peekFirst() { 30 // 取得數組head位置的元素並返回 31 return elements [head]; // elements[head] is null if deque empty 32 }