java源碼-ArrayDeque

開篇

?Deque 接口繼承自 Queue接口鼻种,但 Deque 支持同時從兩端添加或移除元素反番,因此又被成為雙端隊列。鑒于此叉钥,Deque 接口的實現(xiàn)可以被當作 FIFO隊列使用罢缸,也可以當作LIFO隊列(棧)來使用。官方也是推薦使用 Deque 的實現(xiàn)來替代 Stack投队。

?ArrayDeque 是 Deque 接口的一種具體實現(xiàn)枫疆,是依賴于可變數(shù)組來實現(xiàn)的。ArrayDeque 沒有容量限制敷鸦,可根據(jù)需求自動進行擴容息楔。ArrayDeque不支持值為 null 的元素。


ArrayDeque類圖

ArrayDeque類圖


ArrayDeque的類變量和構(gòu)造函數(shù)

?ArrayDeque的類變量當中數(shù)組elements用來保存隊列元素轧膘,head指針指向第一個存儲元素钞螟,tail指向最后一個元素的下一個位置。
?ArrayDeque的calculateSize的邏輯非常巧妙谎碍,用于計算大于numElements的最小的2*n次方的值鳞滨。

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

    // ArrayDeque采用數(shù)組來保存元素,通過頭尾指針實現(xiàn)循環(huán)數(shù)組
    transient Object[] elements; // non-private to simplify nested class access
    
    // 第一個元素和最后一個元素的位置
    transient int head;
    transient int tail;

    private static final int MIN_INITIAL_CAPACITY = 8;
    
    // 計算數(shù)組大小的方式蟆淀,實現(xiàn)大于numElements的最小的2*n次方的數(shù)字
    private static int calculateSize(int numElements) {
        int initialCapacity = MIN_INITIAL_CAPACITY;

        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
        }
        return initialCapacity;
    }
    
    // 分配數(shù)組的大小拯啦,calculateSize計算大小
    private void allocateElements(int numElements) {
        elements = new Object[calculateSize(numElements)];
    }


ArrayDeque的add相關(guān)操作

?ArrayDeque的add操作支持head端插入和tail端插入澡匪,head端插入是先計算位置后插入元素,tail端的插入是先保存元素后計算位置褒链,所以會造成head的指針指向第一個元素的位置唁情,tail指向最后一個元素的下一個位置。
?ArrayDeque的擴容時機是在head和tail相等的時候甫匹,根據(jù)上面分析我們可以得出ArrayDeque的容量達到(舊容量-1)的時候進行擴容甸鸟。
?ArrayDeque的頭部插入過程是回退head指針后添加元素。
?ArrayDeque的尾部插入過程是添加元素后前進tail指針兵迅。

    public boolean offerFirst(E e) {
        addFirst(e);
        return true;
    }

    public void addFirst(E e) {
        if (e == null)
            throw new NullPointerException();
        //head往后移動一個位置放置新插入的元素
        //head指向第一個元素的下標
        elements[head = (head - 1) & (elements.length - 1)] = e;
        if (head == tail)
            doubleCapacity();
    }




    public boolean offerLast(E e) {
        addLast(e);
        return true;
    }
    
    //tail往前移動一個位置放置新插入的元素
    //tail指向末尾元素的下一個位置抢韭,注意是末尾元素的下一個位置
    public void addLast(E e) {
        if (e == null)
            throw new NullPointerException();
        elements[tail] = e;
        //tail循環(huán)以后和head保持一個空余位置,也就是說head=tail+1的時候進行擴容
        if ( (tail = (tail + 1) & (elements.length - 1)) == head)
            doubleCapacity();
    }


ArrayDeque的remove相關(guān)操作

?ArrayDeque的remove操作包括從head開始刪除和從tail開始刪除恍箭。

  • 頭部刪除pollFirst()方法返回head指針指向的元素同時向后移動一個位置
  • 尾部刪除pollLast()方法返回tail指針指向位置的前一個位置的元素后tail指針往前移動一個位置
    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;
        @SuppressWarnings("unchecked")
        E result = (E) 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);
        @SuppressWarnings("unchecked")
        E result = (E) elements[t];
        if (result == null)
            return null;
        elements[t] = null;
        tail = t;
        return result;
    }

    public E getFirst() {
        @SuppressWarnings("unchecked")
        E result = (E) elements[head];
        if (result == null)
            throw new NoSuchElementException();
        return result;
    }

    public E getLast() {
        @SuppressWarnings("unchecked")
        E result = (E) elements[(tail - 1) & (elements.length - 1)];
        if (result == null)
            throw new NoSuchElementException();
        return result;
    }

    @SuppressWarnings("unchecked")
    public E peekFirst() {
        // elements[head] is null if deque empty
        return (E) elements[head];
    }

    @SuppressWarnings("unchecked")
    public E peekLast() {
        return (E) elements[(tail - 1) & (elements.length - 1)];
    }


ArrayDeque的擴容過程

?ArrayDeque的擴容過程如下:

  • 以2倍速率進行擴容(int newCapacity = n << 1)
  • 拷貝下標head到數(shù)組末尾的元素到新數(shù)組
  • 拷貝下標0到tail指針的元素到新數(shù)組
    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 = a;
        head = 0;
        tail = n;
    }


ArrayDeque的操作圖解過程

ArrayDeque添加過程

ArrayDeque擴容過程


參考文章

Java 容器源碼分析之 Deque 與 ArrayDeque

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末刻恭,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子扯夭,更是在濱河造成了極大的恐慌鳍贾,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件交洗,死亡現(xiàn)場離奇詭異骑科,居然都是意外死亡,警方通過查閱死者的電腦和手機构拳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門纵散,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人隐圾,你說我怎么就攤上這事伍掀。” “怎么了暇藏?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵蜜笤,是天一觀的道長。 經(jīng)常有香客問我盐碱,道長把兔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任瓮顽,我火速辦了婚禮县好,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘暖混。我一直安慰自己缕贡,他們只是感情好,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著晾咪,像睡著了一般收擦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谍倦,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天塞赂,我揣著相機與錄音,去河邊找鬼昼蛀。 笑死宴猾,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的叼旋。 我是一名探鬼主播鳍置,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼送淆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起怕轿,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤偷崩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后撞羽,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阐斜,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年诀紊,在試婚紗的時候發(fā)現(xiàn)自己被綠了谒出。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡邻奠,死狀恐怖笤喳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情碌宴,我是刑警寧澤杀狡,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站贰镣,受9級特大地震影響呜象,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜碑隆,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一恭陡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧上煤,春花似錦休玩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽牧抽。三九已至,卻和暖如春遥赚,著一層夾襖步出監(jiān)牢的瞬間扬舒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工凫佛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留讲坎,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓愧薛,卻偏偏與公主長得像晨炕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子毫炉,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容