Java 源碼閱讀的第一步是Collection框架源碼,這也是面試基礎中的基礎; 針對Collection的源碼閱讀寫一個系列的文章,本文是第三篇ArrayDeque。 ---@pdaijava
JDK 1.8.0_110程序員
insert
, remove
和examine
操做,因爲Deque是雙向的,因此能夠對隊列的頭和尾都進行操做,它同時也支持兩組格式,一組是拋出異常的實現;另一組是返回值的實現(沒有則返回null);null
元素。ArrayDeque實現的接口和繼承的類以下:面試
public class ArrayDeque<E> extends AbstractCollection<E> implements Deque<E>, Cloneable, Serializable { }
Collection接口操做分爲以下幾類:數組
Queue接口繼承自Collection接口,除了最基本的Collection的方法以外,它還支持額外的insertion, extraction和inspection操做。這裏有兩組格式,共6個方法,一組是拋出異常的實現;另一組是返回值的實現(沒有則返回null)。安全
Throws exception | Returns special value | |
---|---|---|
Insert | add(e) |
offer(e) |
Remove | remove() |
poll() |
Examine | element() |
peek() |
Deque 繼承自 Queue接口,除了支持Queue的方法以外,還支持insert
, remove
和examine
操做,因爲Deque是雙向的,因此能夠對隊列的頭和尾都進行操做,它同時也支持兩組格式,一組是拋出異常的實現;另一組是返回值的實現(沒有則返回null)。共12個方法以下:數據結構
First Element (Head) | Last Element (Tail) | ||
---|---|---|---|
Throws exception | Special value | Throws exception | |
Insert | addFirst(e) |
offerFirst(e) |
addLast(e) |
Remove | removeFirst() |
pollFirst() |
removeLast() |
Examine | getFirst() |
peekFirst() |
getLast() |
當把Deque
當作FIFO的queue
來使用時,元素是從deque
的尾部添加,從頭部進行刪除的; 因此deque
的部分方法是和queue
是等同的。具體以下:併發
Queue Method | Equivalent Deque Method |
---|---|
add(e) |
addLast(e) |
offer(e) |
offerLast(e) |
remove() |
removeFirst() |
poll() |
pollFirst() |
element() |
getFirst() |
peek() |
peekFirst() |
Deque的含義是「double ended queue」,即雙端隊列,它既能夠看成棧使用,也能夠看成隊列使用。app
下表列出了Deque與Queue相對應的接口:框架
Queue Method | Equivalent Deque Method | 說明 |
---|---|---|
add(e) |
addLast(e) |
向隊尾插入元素,失敗則拋出異常 |
offer(e) |
offerLast(e) |
向隊尾插入元素,失敗則返回false |
remove() |
removeFirst() |
獲取並刪除隊首元素,失敗則拋出異常 |
poll() |
pollFirst() |
獲取並刪除隊首元素,失敗則返回null |
element() |
getFirst() |
獲取但不刪除隊首元素,失敗則拋出異常 |
peek() |
peekFirst() |
獲取但不刪除隊首元素,失敗則返回null |
下表列出了Deque與Stack對應的接口:ide
Stack Method | Equivalent Deque Method | 說明 |
---|---|---|
push(e) |
addFirst(e) |
向棧頂插入元素,失敗則拋出異常 |
無 | offerFirst(e) |
向棧頂插入元素,失敗則返回false |
pop() |
removeFirst() |
獲取並刪除棧頂元素,失敗則拋出異常 |
無 | pollFirst() |
獲取並刪除棧頂元素,失敗則返回null |
peek() |
peekFirst() |
獲取但不刪除棧頂元素,失敗則拋出異常 |
無 | peekFirst() |
獲取但不刪除棧頂元素,失敗則返回null |
上面兩個表共定義了Deque的12個接口。添加,刪除,取值都有兩套接口,它們功能相同,區別是對失敗狀況的處理不一樣。一套接口遇到失敗就會拋出異常,另外一套遇到失敗會返回特殊值(false
或null
)。除非某種實現對容量有限制,大多數狀況下,添加操做是不會失敗的。雖然Deque的接口有12個之多,但無非就是對容器的兩端進行操做,或添加,或刪除,或查看。明白了這一點講解起來就會很是簡單。
從名字能夠看出ArrayDeque底層經過數組實現,爲了知足能夠同時在數組兩端插入或刪除元素的需求,該數組還必須是循環的,即循環數組(circular array),也就是說數組的任何一點均可能被看做起點或者終點。ArrayDeque是非線程安全的(not thread-safe),當多個線程同時使用的時候,須要程序員手動同步;另外,該容器不容許放入null
元素。
/** * The array in which the elements of the deque are stored. * The capacity of the deque is the length of this array, which is * always a power of two. The array is never allowed to become * full, except transiently within an addX method where it is * resized (see doubleCapacity) immediately upon becoming full, * thus avoiding head and tail wrapping around to equal each * other. We also guarantee that all array cells not holding * deque elements are always null. */ transient Object[] elements; // non-private to simplify nested class access /** * 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; /** * The minimum capacity that we'll use for a newly created deque. * Must be a power of 2. */ private static final int MIN_INITIAL_CAPACITY = 8;
head
指向首端第一個有效元素,tail
指向尾端第一個能夠插入元素的空位。由於是循環數組,因此head
不必定總等於0,tail
也不必定老是比head
大。
下面再說說擴容函數doubleCapacity(),其邏輯是申請一個更大的數組(原數組的兩倍),而後將原數組複製過去。
/** * Allocates empty array to hold the given number of elements. * * @param numElements the number of elements to hold */ 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 = new Object[initialCapacity]; } /** * Doubles the capacity of this deque. Call only when full, i.e., * when head and tail have wrapped around to become equal. */ private void doubleCapacity() { assert head == tail; int p = head; int n = elements.length; int r = n - p; // head右邊元素的個數 int newCapacity = n << 1;//原空間的2倍 if (newCapacity < 0) throw new IllegalStateException("Sorry, deque too big"); Object[] a = new Object[newCapacity]; System.arraycopy(elements, p, a, 0, r);//複製右半部分,對應上圖中綠色部分 System.arraycopy(elements, 0, a, r, p);//複製左半部分,對應上圖中灰色部分 elements = (E[])a; head = 0; tail = n; } /** * Copies the elements from our element array into the specified array, * in order (from first to last element in the deque). It is assumed * that the array is large enough to hold all elements in the deque. * * @return its argument */ private <T> T[] copyElements(T[] a) { if (head < tail) { System.arraycopy(elements, head, a, 0, size()); } else if (head > tail) { int headPortionLen = elements.length - head; System.arraycopy(elements, head, a, 0, headPortionLen); System.arraycopy(elements, 0, a, headPortionLen, tail); } return a; }
/** * Constructs an empty array deque with an initial capacity * sufficient to hold 16 elements. */ public ArrayDeque() { elements = new Object[16]; } /** * Constructs an empty array deque with an initial capacity * sufficient to hold the specified number of elements. * * @param numElements lower bound on initial capacity of the deque */ public ArrayDeque(int numElements) { allocateElements(numElements); } /** * Constructs a deque containing the elements of the specified * collection, in the order they are returned by the collection's * iterator. (The first element returned by the collection's * iterator becomes the first element, or <i>front</i> of the * deque.) * * @param c the collection whose elements are to be placed into the deque * @throws NullPointerException if the specified collection is null */ public ArrayDeque(Collection<? extends E> c) { allocateElements(c.size()); addAll(c); }
主要都是基於以下四個方法addFirst(), addLast(), pollFirst(), pollLast()
addFirst(E e)
的做用是在Deque的首端插入元素,也就是在head
的前面插入元素,在空間足夠且下標沒有越界的狀況下,只須要將elements[--head] = e
便可。
實際須要考慮:1.空間是否夠用,以及2.下標是否越界的問題。
//addFirst(E e) public void addFirst(E e) { if (e == null)//不容許放入null throw new NullPointerException(); elements[head = (head - 1) & (elements.length - 1)] = e;//2.下標是否越界 if (head == tail)//1.空間是否夠用 doubleCapacity();//擴容 }
上述代碼咱們看到,空間問題是在插入以後解決的,由於tail
老是指向下一個可插入的空位,也就意味着elements
數組至少有一個空位,因此插入元素的時候不用考慮空間問題。
下標越界的處理解決起來很是簡單,head = (head - 1) & (elements.length - 1)
就能夠了,這段代碼至關於取餘,同時解決了head
爲負值的狀況。由於elements.length
必需是2
的指數倍,elements - 1
就是二進制低位全1
,跟head - 1
相與以後就起到了取模的做用,若是head - 1
爲負數(其實只多是-1),則至關於對其取相對於elements.length
的補碼。
addLast(E e)
的做用是在Deque的尾端插入元素,也就是在tail
的位置插入元素,因爲tail
老是指向下一個能夠插入的空位,所以只須要elements[tail] = e;
便可。插入完成後再檢查空間,若是空間已經用光,則調用doubleCapacity()
進行擴容。
public void addLast(E e) { if (e == null)//不容許放入null throw new NullPointerException(); elements[tail] = e;//賦值 if ( (tail = (tail + 1) & (elements.length - 1)) == head)//下標越界處理 doubleCapacity();//擴容 }
pollFirst()
的做用是刪除並返回Deque首端元素,也便是head
位置處的元素。若是容器不空,只須要直接返回elements[head]
便可,固然還須要處理下標的問題。因爲ArrayDeque
中不容許放入null
,當elements[head] == null
時,意味着容器爲空。
public E pollFirst() { E result = elements[head]; if (result == null)//null值意味着deque爲空 return null; elements[h] = null;//let GC work head = (head + 1) & (elements.length - 1);//下標越界處理 return result; }
pollLast()
的做用是刪除並返回Deque尾端元素,也便是tail
位置前面的那個元素。
public E pollLast() { int t = (tail - 1) & (elements.length - 1);//tail的上一個位置是最後一個元素 E result = elements[t]; if (result == null)//null值意味着deque爲空 return null; elements[t] = null;//let GC work tail = t; return result; }
peekFirst()
的做用是返回但不刪除Deque首端元素,也便是head
位置處的元素,直接返回elements[head]
便可。
public E peekFirst() { return elements[head]; // elements[head] is null if deque empty }
peekLast()
的做用是返回但不刪除Deque尾端元素,也便是tail
位置前面的那個元素。
public E peekLast() { return elements[(tail - 1) & (elements.length - 1)]; }
/** * Inserts the specified element at the end of this deque. * * <p>This method is equivalent to {@link #addLast}. * * @param e the element to add * @return {@code true} (as specified by {@link Collection#add}) * @throws NullPointerException if the specified element is null */ public boolean add(E e) { addLast(e); return true; }
/** * Inserts the specified element at the end of this deque. * * <p>This method is equivalent to {@link #offerLast}. * * @param e the element to add * @return {@code true} (as specified by {@link Queue#offer}) * @throws NullPointerException if the specified element is null */ public boolean offer(E e) { return offerLast(e); }
/** * Retrieves and removes the head of the queue represented by this deque. * * This method differs from {@link #poll poll} only in that it throws an * exception if this deque is empty. * * <p>This method is equivalent to {@link #removeFirst}. * * @return the head of the queue represented by this deque * @throws NoSuchElementException {@inheritDoc} */ public E remove() { return removeFirst(); }
/** * Retrieves and removes the head of the queue represented by this deque * (in other words, the first element of this deque), or returns * {@code null} if this deque is empty. * * <p>This method is equivalent to {@link #pollFirst}. * * @return the head of the queue represented by this deque, or * {@code null} if this deque is empty */ public E poll() { return pollFirst(); }
/** * Retrieves, but does not remove, the head of the queue represented by * this deque. This method differs from {@link #peek peek} only in * that it throws an exception if this deque is empty. * * <p>This method is equivalent to {@link #getFirst}. * * @return the head of the queue represented by this deque * @throws NoSuchElementException {@inheritDoc} */ public E element() { return getFirst(); }
/** * Retrieves, but does not remove, the head of the queue represented by * this deque, or returns {@code null} if this deque is empty. * * <p>This method is equivalent to {@link #peekFirst}. * * @return the head of the queue represented by this deque, or * {@code null} if this deque is empty */ public E peek() { return peekFirst(); }
/** * Pushes an element onto the stack represented by this deque. In other * words, inserts the element at the front of this deque. * * <p>This method is equivalent to {@link #addFirst}. * * @param e the element to push * @throws NullPointerException if the specified element is null */ public void push(E e) { addFirst(e); }
/** * Pops an element from the stack represented by this deque. In other * words, removes and returns the first element of this deque. * * <p>This method is equivalent to {@link #removeFirst()}. * * @return the element at the front of this deque (which is the top * of the stack represented by this deque) * @throws NoSuchElementException {@inheritDoc} */ public E pop() { return removeFirst(); }
/** * Returns the number of elements in this deque. * * @return the number of elements in this deque */ public int size() { return (tail - head) & (elements.length - 1); }
/** * Returns {@code true} if this deque contains no elements. * * @return {@code true} if this deque contains no elements */ public boolean isEmpty() { return head == tail; }
/** * Returns an iterator over the elements in this deque. The elements * will be ordered from first (head) to last (tail). This is the same * order that elements would be dequeued (via successive calls to * {@link #remove} or popped (via successive calls to {@link #pop}). * * @return an iterator over the elements in this deque */ public Iterator<E> iterator() { return new DeqIterator(); } public Iterator<E> descendingIterator() { return new DescendingIterator(); }
/** * Returns {@code true} if this deque contains the specified element. * More formally, returns {@code true} if and only if this deque contains * at least one element {@code e} such that {@code o.equals(e)}. * * @param o object to be checked for containment in this deque * @return {@code true} if this deque contains the specified element */ public boolean contains(Object o) { if (o == null) return false; int mask = elements.length - 1; int i = head; Object x; while ( (x = elements[i]) != null) { if (o.equals(x)) return true; i = (i + 1) & mask; } return false; }
/** * Removes a single instance of the specified element from this deque. * If the deque does not contain the element, it is unchanged. * More formally, removes the first element {@code e} such that * {@code o.equals(e)} (if such an element exists). * Returns {@code true} if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). * * <p>This method is equivalent to {@link #removeFirstOccurrence(Object)}. * * @param o element to be removed from this deque, if present * @return {@code true} if this deque contained the specified element */ public boolean remove(Object o) { return removeFirstOccurrence(o); }
/** * Removes all of the elements from this deque. * The deque will be empty after this call returns. */ public void clear() { int h = head; int t = tail; if (h != t) { // clear all cells head = tail = 0; int i = h; int mask = elements.length - 1; do { elements[i] = null; i = (i + 1) & mask; } while (i != t); } }
/** * Returns an array containing all of the elements in this deque * in proper sequence (from first to last element). * * <p>The returned array will be "safe" in that no references to it are * maintained by this deque. (In other words, this method must allocate * a new array). The caller is thus free to modify the returned array. * * <p>This method acts as bridge between array-based and collection-based * APIs. * * @return an array containing all of the elements in this deque */ public Object[] toArray() { return copyElements(new Object[size()]); }
/** * Returns an array containing all of the elements in this deque in * proper sequence (from first to last element); the runtime type of the * returned array is that of the specified array. If the deque fits in * the specified array, it is returned therein. Otherwise, a new array * is allocated with the runtime type of the specified array and the * size of this deque. * * <p>If this deque fits in the specified array with room to spare * (i.e., the array has more elements than this deque), the element in * the array immediately following the end of the deque is set to * {@code null}. * * <p>Like the {@link #toArray()} method, this method acts as bridge between * array-based and collection-based APIs. Further, this method allows * precise control over the runtime type of the output array, and may, * under certain circumstances, be used to save allocation costs. * * <p>Suppose {@code x} is a deque known to contain only strings. * The following code can be used to dump the deque into a newly * allocated array of {@code String}: * * <pre> {@code String[] y = x.toArray(new String[0]);}</pre> * * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. * * @param a the array into which the elements of the deque are to * be stored, if it is big enough; otherwise, a new array of the * same runtime type is allocated for this purpose * @return an array containing all of the elements in this deque * @throws ArrayStoreException if the runtime type of the specified array * is not a supertype of the runtime type of every element in * this deque * @throws NullPointerException if the specified array is null */ @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { int size = size(); if (a.length < size) a = (T[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); copyElements(a); if (a.length > size) a[size] = null; return a; }