JDK之ArrayDeque源碼解析

剛入java不久的程序猿,對於簡單的使用已毫不滿足,最終爲了一探究竟,翻開了JDK的源碼,以下觀點爲自己的理解及看了多篇博客的總結,歡迎各位大神指出不對的地方,當然也歡迎和我一樣剛學的同學,一起加油努力吧~~

ArrayDeque是什麼

ArrayDeque是什麼?deque英文翻譯爲雙向隊列,ArrayDeque就是實現了Deque接口的一個雙向隊列的集合,ArrayDeque也是由數組實現,數組的任意位置都可以作爲頭或者尾,ArrayDeque不允許元素爲null

ArrayDeque源碼解析

在上面我們瞭解到了ArrayDeque是什麼,大致在腦海中留下初步的印象,接下來我們來看一下源碼,由於Deque繼承了Queue接口,這裏我們就先來看下這個接口裏定義的方法

public interface Queue<E> extends Collection<E> {
    /**
     * 插入元素,成功返回true,失敗拋出異常
     */
    boolean add(E e);

    /**
     * 插入元素,成功返回true,失敗返回false
     */
    boolean offer(E e);

    /**
     * 獲取並刪除隊首元素,隊列爲空則拋出異常
     */
    E remove();

    /**
     * 獲取並刪除隊首元素,隊列爲空是返回null
     */
    E poll();

    /**
     * 獲取但不刪除隊首元素,隊列爲空拋出異常
     */
    E element();

    /**
     * 獲取但不刪除隊首元素,隊列爲空返回null
     */
    E peek();
}

這裏的方法已經註釋過,大家可以自己看下每個方法的作用,還有就是Queue是繼承了Collection接口的,說明最終扔是以迭代的方式來完成的,好了,我們在大致看下Deque接口定義的方法

public interface Deque<E> extends Queue<E> {
    /**
     * 在隊列最前端插入元素,無返回值
     */
    void addFirst(E e);

    /**
     * 在隊列最後端插入元素,無返回值
     */
    void addLast(E e);

    /**
     * 在隊列最前端插入元素,成功返回true失敗返回false
     */
    boolean offerFirst(E e);

    /**
     * 在隊列最後端插入元素,成功返回true失敗返回false
     */
    boolean offerLast(E e);

    /**
     * 獲取並刪除隊列最前端元素,隊列爲空拋出異常
     */
    E removeFirst();

    /**
     * 獲取並刪除隊列最後端元素,隊列爲空拋出異常
     */
    E removeLast();

    /**
     * 獲取並刪除隊列最前端元素,隊列爲空返回null
     */
    E pollFirst();

    /**
     * 獲取並刪除隊列最後端元素,隊列爲空返回null
     */
    E pollLast();

    /**
     * 獲取但不刪除隊列最前端元素,隊列爲空拋出異常
     */
    E getFirst();

    /**
     * 獲取但不刪除隊列最後端元素,隊列爲空拋出異常
     */
    E getLast();

    /**
     * 獲取但不刪除隊列最前端元素,隊列爲空返回null
     */
    E peekFirst();

    /**
     * 獲取但不刪除隊列最後端元素,隊列爲空返回null
     */
    E peekLast();

    /**
     * 刪除隊列裏第一次出現的指定元素
     */
    boolean removeFirstOccurrence(Object o);

    /**
     * 刪除隊列裏最後一次出現的指定元素
     */
    boolean removeLastOccurrence(Object o);

    // *** Queue methods ***

    boolean add(E e);

    boolean offer(E e);

    E remove();

    E poll();

    E element();

    E peek();

    // *** Stack methods ***

    /**
     * 內存空間允許的情況下,將元素放入出現在棧中的Dqueue中
     * 成功返回true,失敗拋出異常
     */
    void push(E e);

    /**
     * 刪除並返回這個隊列的第一個元素
     */
    E pop();


    // *** Collection methods ***

    /**
     * 刪除某個元素
     */
    boolean remove(Object o);

    /**
     * 是否包含某個元素
     */
    boolean contains(Object o);

    /**
     * 元素數量
     */
    public int size();

    /**
     * 迭代
     */
    Iterator<E> iterator();

    /**
     * 返回一個順序反轉後的迭代器
     */
    Iterator<E> descendingIterator();
}

上面就是Deque接口裏的方法,看起來方法很多,其實方法無非就是添加,刪除,取值,只不過添加刪除頭部還是尾部以及返回值的不同而已,整個體系看完,接着看最主要的,ArrayDque的源碼

public class ArrayDeque<E> extends AbstractCollection<E>
                           implements Deque<E>, Cloneable, Serializable{
...
}

上面就是ArrayDeque的繼承與實現,接下來看一下定義的變量

    /**
     * 存取隊列元素的數組
     */
    private transient E[] elements;

    /**
     * 隊列的頭
     */
    private transient int head;

    /**
     * 隊列的尾
     */
    private transient int tail;

    /**
     * 創建新的隊列時最小容量
     */
    private static final int MIN_INITIAL_CAPACITY = 8;

這裏隊列的頭與尾,可以說數組中任意位置都可以爲頭或者尾,繼續看代碼

    /**
     * 無參構造,初始化大小爲默認大小16
     */
    public ArrayDeque() {
        elements = (E[]) new Object[16];
    }

    /**
     * 有參構造,參數爲元素數量
     */
    public ArrayDeque(int numElements) {
        allocateElements(numElements);
    }

    /**
     * 有參構造,參數爲集合
     */
    public ArrayDeque(Collection<? extends E> c) {
        allocateElements(c.size());
        addAll(c);
    }

    /**
     * 初始化數組
     */
    private void allocateElements(int numElements) {
        //初始化數組的最小容量
        int initialCapacity = MIN_INITIAL_CAPACITY;
        //找到大於numElements的最小的2的冪整數
        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];
    }

上面是ArrayDeque的構造方法,感覺沒什麼特殊的東西,就是初始化數組,只不過一個是默認的16長度,一個是傳入的參數,最後一個構造在初始化數組後,調用AbstractCollection的addAll方法將集合放入,繼續看幾個比較重要的方法

    /**
     * 擴容方法,當數組滿了進行擴容操作,大小爲原來的兩倍
     */
    private void doubleCapacity() {
        assert head == tail;
        int p = head;
        //元素個數
        int n = elements.length;
        int r = n - p; // number of elements to the right of p
        //將容量擴大一倍
        int newCapacity = n << 1;
        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;
    }

    /**
     * 在隊列的前端添加元素,無返回值
     */
    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();
    }

    /**
     * 在隊列的前端添加元素,調用addFirst方法,返回值true
     */
    public boolean offerFirst(E e) {
        addFirst(e);
        return true;
    }

    /**
     * 在隊列的後端添加元素,調用addLast方法,返回值true
     */
    public boolean offerLast(E e) {
        addLast(e);
        return true;
    }

    /**
     * 獲取並刪除最前端元素
     */
    public E removeFirst() {
        E x = pollFirst();
        //爲空時拋出異常
        if (x == null)
            throw new NoSuchElementException();
        return x;
    }

    /**
     * 獲取並刪除最後端元素
     */
    public E removeLast() {
        E x = pollLast();
        //爲空時拋出異常
        if (x == null)
            throw new NoSuchElementException();
        return x;
    }

    public E pollFirst() {
        int h = head;
        //隊列空爲則元素爲空
        E result = elements[h]; 
        if (result == null)
            return null;
        //head處置空    
        elements[h] = null; 
        //處理特殊情況,當h爲elements.length-1時的情況
        head = (h + 1) & (elements.length - 1);
        return result;
    }

    public E pollLast() {
        //處理特殊情況,當tail爲0時的情況
        int t = (tail - 1) & (elements.length - 1);
        E result = elements[t];
        if (result == null)
            return null;
        elements[t] = null;
        //下一個需要添加的下標
        tail = t;
        return result;
    }

    /**
     * 獲取但不刪除隊列最前端元素
     */
    public E getFirst() {
        E x = elements[head];
        //隊列爲空拋出異常
        if (x == null)
            throw new NoSuchElementException();
        return x;
    }

    /**
     * 獲取但不刪除隊列最後端元素
     */
    public E getLast() {
        E x = elements[(tail - 1) & (elements.length - 1)];
        //隊列爲空拋出異常
        if (x == null)
            throw new NoSuchElementException();
        return x;
    }

    /**
     * 獲取但不刪除隊列最前端元素,隊列爲空返回null
     */
    public E peekFirst() {
        return elements[head]; 
    }

    /**
     * 獲取但不刪除隊列最後端元素,隊列爲空返回null
     */
    public E peekLast() {
        return elements[(tail - 1) & (elements.length - 1)];
    }

好了,這裏列出了ArrayDeque最主要的一些方法,其他方法大多都比較簡單了,大家可以自己去翻一下源碼,最後我們需要注意的是ArrayDeque存元素時,不能存入null元素,否則會報空指針異常,好了,ArrayDeque的解析到這裏就結束了

發佈了22 篇原創文章 · 獲贊 11 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章