最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java源碼刨析之ArrayDeque

 更新時(shí)間:2022年07月22日 10:58:26   作者:一無(wú)是處的研究僧  
ArrayDeque是Deque接口的一個(gè)實(shí)現(xiàn),使用了可變數(shù)組,所以沒(méi)有容量上的限制。同時(shí),?ArrayDeque是線程不安全的,在沒(méi)有外部同步的情況下,不能再多線程環(huán)境下使用<BR>

前言

在本篇文章當(dāng)中主要跟大家介紹JDK給我們提供的一種用數(shù)組實(shí)現(xiàn)的雙端隊(duì)列,在之前的文章LinkedList源碼剖析當(dāng)中我們已經(jīng)介紹了一種雙端隊(duì)列,不過(guò)與ArrayDeque不同的是,LinkedList的雙端隊(duì)列使用雙向鏈表實(shí)現(xiàn)的。

雙端隊(duì)列整體分析

我們通常所談?wù)摰降年?duì)列都是一端進(jìn)一端出,而雙端隊(duì)列的兩端則都是可進(jìn)可出。下面是雙端隊(duì)列的幾個(gè)操作:

數(shù)據(jù)從雙端隊(duì)列左側(cè)進(jìn)入。

數(shù)據(jù)從雙端隊(duì)列右側(cè)進(jìn)入。

數(shù)據(jù)從雙端隊(duì)列左側(cè)彈出。

數(shù)據(jù)從雙端隊(duì)列右側(cè)彈出。

而在ArrayDeque當(dāng)中也給我們提供了對(duì)應(yīng)的方法去實(shí)現(xiàn),比如下面這個(gè)例子就是上圖對(duì)應(yīng)的代碼操作:

public void test() {
    ArrayDeque<Integer> deque = new ArrayDeque<>();
    deque.addLast(100);
    System.out.println(deque);
    deque.addFirst(55);
    System.out.println(deque);
    deque.addLast(-55);
    System.out.println(deque);
    deque.removeFirst();
    System.out.println(deque);
    deque.removeLast();
    System.out.println(deque);
}

// 輸出結(jié)果
[100]
[55, 100]
[55, 100, -55]
[100, -55]
[100]

數(shù)組實(shí)現(xiàn)ArrayDeque(雙端隊(duì)列)的原理

ArrayDeque底層是使用數(shù)組實(shí)現(xiàn)的,而且數(shù)組的長(zhǎng)度必須是2的整數(shù)次冪,這么操作的原因是為了后面位運(yùn)算好操作。在ArrayDeque當(dāng)中有兩個(gè)整形變量headtail,分別指向右側(cè)的第一個(gè)進(jìn)入隊(duì)列的數(shù)據(jù)和左側(cè)第一個(gè)進(jìn)行隊(duì)列的數(shù)據(jù),整個(gè)內(nèi)存布局如下圖所示:

其中tail指的位置沒(méi)有數(shù)據(jù),head指的位置存在數(shù)據(jù)。

當(dāng)我們需要從左往右增加數(shù)據(jù)時(shí)(入隊(duì)),內(nèi)存當(dāng)中數(shù)據(jù)變化情況如下:

當(dāng)我們需要從右往做左增加數(shù)據(jù)時(shí)(入隊(duì)),內(nèi)存當(dāng)中數(shù)據(jù)變化情況如下:

當(dāng)我們需要從右往左刪除數(shù)據(jù)時(shí)(出隊(duì)),內(nèi)存當(dāng)中數(shù)據(jù)變化情況如下:

當(dāng)我們需要從左往右刪除數(shù)據(jù)時(shí)(出隊(duì)),內(nèi)存當(dāng)中數(shù)據(jù)變化情況如下:

底層數(shù)據(jù)遍歷順序和邏輯順序

上面主要談?wù)摰降臄?shù)組在內(nèi)存當(dāng)中的布局,但是他是具體的物理存儲(chǔ)數(shù)據(jù)的順序,這個(gè)順序和我們的邏輯上的順序是不一樣的,根據(jù)上面的插入順序,我們可以畫(huà)出下面的圖,大家可以仔細(xì)分析一下這個(gè)圖的順序問(wèn)題。

上圖當(dāng)中隊(duì)列左側(cè)的如隊(duì)順序是0, 1, 2, 3,右側(cè)入隊(duì)的順序?yàn)?5, 14, 13, 12, 11, 10, 9, 8,因此在邏輯上我們的隊(duì)列當(dāng)中的數(shù)據(jù)布局如下圖所示:

根據(jù)前面一小節(jié)談到的輸入在入隊(duì)的時(shí)候數(shù)組當(dāng)中數(shù)據(jù)的變化我們可以知道,數(shù)據(jù)在數(shù)組當(dāng)中的布局為:

ArrayDeque類(lèi)關(guān)鍵字段分析

// 底層用于存儲(chǔ)具體數(shù)據(jù)的數(shù)組
transient Object[] elements;
// 這就是前面談到的 head
transient int head;
// 與上文談到的 tail 含義一樣
transient int tail;
// MIN_INITIAL_CAPACITY 表示數(shù)組 elements 的最短長(zhǎng)度
private static final int MIN_INITIAL_CAPACITY = 8;

以上就是ArrayDeque當(dāng)中的最主要的字段,其含義還是比較容易理解的!

ArrayDeque構(gòu)造函數(shù)分析

默認(rèn)構(gòu)造函數(shù),數(shù)組默認(rèn)申請(qǐng)的長(zhǎng)度為16

public ArrayDeque() {
    elements = new Object[16];
}

指定數(shù)組長(zhǎng)度的初始化長(zhǎng)度,下面列出了改構(gòu)造函數(shù)涉及的所有函數(shù)。

public ArrayDeque(int numElements) {
    allocateElements(numElements);
}
private void allocateElements(int numElements) {
    elements = new Object[calculateSize(numElements)];
}
private static int calculateSize(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
    }
    return initialCapacity;
}

上面的最難理解的就是函數(shù)calculateSize了,他的主要作用是如果用戶(hù)輸入的長(zhǎng)度小于MIN_INITIAL_CAPACITY時(shí),返回MIN_INITIAL_CAPACITY。否則返回比initialCapacity大的第一個(gè)是2的整數(shù)冪的整數(shù),比如說(shuō)如果輸入的是9返回的16,輸入4返回8。

calculateSize的代碼還是很難理解的,讓我們一點(diǎn)一點(diǎn)的來(lái)分析。首先我們使用一個(gè)2的整數(shù)次冪的數(shù)進(jìn)行上面移位操作的操作!

從上圖當(dāng)中我們會(huì)發(fā)現(xiàn),我們?cè)谝粋€(gè)數(shù)的二進(jìn)制數(shù)的32位放一個(gè)1,經(jīng)過(guò)移位之后最終32位的比特?cái)?shù)字全部變成了1。根據(jù)上面數(shù)字變化的規(guī)律我們可以發(fā)現(xiàn),任何一個(gè)比特經(jīng)過(guò)上面移位的變化,這個(gè)比特后面的31個(gè)比特位都會(huì)變成1,像下圖那樣:

因此上述的移位操作的結(jié)果只取決于最高一位的比特值為1,移位操作后它后面的所有比特位的值全為1,而在上面函數(shù)的最后,我們返回的結(jié)果就是上面移位之后的結(jié)果 +1。又因?yàn)橐莆恢笞罡呶坏?code>1到最低位的1之間的比特值全為1,當(dāng)我們+1之后他會(huì)不斷的進(jìn)位,最終只有一個(gè)比特位置是1,因此它是2的整數(shù)倍。

經(jīng)過(guò)上述過(guò)程分析,我們就可以立即函數(shù)calculateSize了。

ArrayDeque關(guān)鍵函數(shù)分析

addLast函數(shù)分析

// tail 的初始值為 0 
public void addLast(E e) {
    if (e == null)
        throw new NullPointerException();
    elements[tail] = e;
    // 這里進(jìn)行的 & 位運(yùn)算 相當(dāng)于取余數(shù)操作
    // (tail + 1) & (elements.length - 1) == (tail + 1) % elements.length
    // 這個(gè)操作主要是用于判斷數(shù)組是否滿(mǎn)了,如果滿(mǎn)了則需要擴(kuò)容
    // 同時(shí)這個(gè)操作將 tail + 1,即 tail = tail + 1
    if ( (tail = (tail + 1) & (elements.length - 1)) == head)
        doubleCapacity();
}

代碼(tail + 1) & (elements.length - 1) == (tail + 1) % elements.length成立的原因是任意一個(gè)數(shù) a a a對(duì) 2 n 2^n 2n進(jìn)行取余數(shù)操作和 a a a跟 2 n − 1 2^n - 1 2n−1進(jìn)行&運(yùn)算的結(jié)果相等,即:

從上面的代碼來(lái)看下標(biāo)為tail的位置是沒(méi)有數(shù)據(jù)的,是一個(gè)空位置。

addFirst函數(shù)分析

// head 的初始值為 0 
public void addFirst(E e) {
    if (e == null)
        throw new NullPointerException();
    // 若此時(shí)數(shù)組長(zhǎng)度elements.length = 16
    // 那么下面代碼執(zhí)行過(guò)后 head = 15
    // 下面代碼的操作結(jié)果和下面兩行代碼含義一致
    // elements[(head - 1 + elements.length) % elements.length] = e
    // head = (head - 1 + elements.length) % elements.length
    elements[head = (head - 1) & (elements.length - 1)] = e;
    if (head == tail)
        doubleCapacity();
}

上面代碼操作結(jié)果和上文當(dāng)中我們提到的,在隊(duì)列當(dāng)中從右向左加入數(shù)據(jù)一樣。從上面的代碼看,我們可以發(fā)現(xiàn)下標(biāo)為head的位置是存在數(shù)據(jù)的。

doubleCapacity函數(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];
    // arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length)
    // 上面是函數(shù) System.arraycopy 的函數(shù)參數(shù)列表
    // 大家可以參考上面理解下面的拷貝代碼
    System.arraycopy(elements, p, a, 0, r);
    System.arraycopy(elements, 0, a, r, p);
    elements = a;
    head = 0;
    tail = n;
}

上面的代碼還是比較簡(jiǎn)單的,這里給大家一個(gè)圖示,大家就更加容易理解了:

擴(kuò)容之后將原來(lái)數(shù)組的數(shù)據(jù)拷貝到了新數(shù)組當(dāng)中,雖然數(shù)據(jù)在舊數(shù)組和新數(shù)組當(dāng)中的順序發(fā)生變化了,但是他們的相對(duì)順序卻沒(méi)有發(fā)生變化,他們的邏輯順序也是一樣的,這里的邏輯可能有點(diǎn)繞,大家在這里可以好好思考一下。

pollLast和pollFirst函數(shù)分析

這兩個(gè)函數(shù)的代碼就比較簡(jiǎn)單了,大家可以根據(jù)前文所談到的內(nèi)容和圖示去理解下面的代碼。

public E pollLast() {
    // 計(jì)算出待刪除的數(shù)據(jù)的下標(biāo)
    int t = (tail - 1) & (elements.length - 1);
    @SuppressWarnings("unchecked")
    E result = (E) elements[t];
    if (result == null)
        return null;
    // 將需要?jiǎng)h除的數(shù)據(jù)的下標(biāo)值設(shè)置為 null 這樣這塊內(nèi)存就
    // 可以被回收了
    elements[t] = null;
    tail = t;
    return result;
}
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;
}

總結(jié)

在本篇文章當(dāng)中,主要跟大家分享了ArrayDeque的設(shè)計(jì)原理,和他的底層實(shí)現(xiàn)過(guò)程。ArrayDeque底層數(shù)組當(dāng)中的數(shù)據(jù)順序和隊(duì)列的邏輯順序這部分可能比較抽象,大家可以根據(jù)圖示好好體會(huì)一下?。?!

到此這篇關(guān)于Java源碼刨析之ArrayDeque的文章就介紹到這了,更多相關(guān)Java ArrayDeque內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 老生常談JVM的內(nèi)存溢出說(shuō)明及參數(shù)調(diào)整

    老生常談JVM的內(nèi)存溢出說(shuō)明及參數(shù)調(diào)整

    下面小編就為大家?guī)?lái)一篇老生常談JVM的內(nèi)存溢出說(shuō)明及參數(shù)調(diào)整。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-03-03
  • SpringBoot使用@Cacheable出現(xiàn)預(yù)覽工具亂碼的解決方法

    SpringBoot使用@Cacheable出現(xiàn)預(yù)覽工具亂碼的解決方法

    直接使用注解進(jìn)行緩存數(shù)據(jù),我們?cè)偈褂霉ぞ呷ヮA(yù)覽存儲(chǔ)的數(shù)據(jù)時(shí)發(fā)現(xiàn)是亂碼,這是由于默認(rèn)序列化的問(wèn)題,所以接下來(lái)將給大家介紹一下SpringBoot使用@Cacheable出現(xiàn)預(yù)覽工具亂碼的解決方法,需要的朋友可以參考下
    2023-10-10
  • MyBatis多對(duì)多關(guān)聯(lián)映射創(chuàng)建示例

    MyBatis多對(duì)多關(guān)聯(lián)映射創(chuàng)建示例

    這篇文章主要為大家介紹了MyBatis多對(duì)多關(guān)聯(lián)映射的創(chuàng)建示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • java中queue接口的使用詳解

    java中queue接口的使用詳解

    本篇文章主要介紹了java中queue接口的使用詳解,對(duì)學(xué)習(xí)Queue接口有一定的幫助,感興趣的小伙伴們可以參考一下。
    2016-11-11
  • Java必備知識(shí)之位運(yùn)算及常見(jiàn)進(jìn)制解讀

    Java必備知識(shí)之位運(yùn)算及常見(jiàn)進(jìn)制解讀

    從現(xiàn)代計(jì)算機(jī)中所有的數(shù)據(jù)二進(jìn)制的形式存儲(chǔ)在設(shè)備中。即 0、1 兩種狀態(tài),計(jì)算機(jī)對(duì)二進(jìn)制數(shù)據(jù)進(jìn)行的運(yùn)算(+、-、*、/)都是叫位運(yùn)算,即將符號(hào)位共同參與運(yùn)算的運(yùn)算
    2021-10-10
  • Java集合TreeSet用法詳解

    Java集合TreeSet用法詳解

    本文詳細(xì)講解了Java集合TreeSet用法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-12-12
  • java單例五種實(shí)現(xiàn)模式解析

    java單例五種實(shí)現(xiàn)模式解析

    這篇文章主要介紹了java單例五種實(shí)現(xiàn)模式解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • Spring Boot 與 Vue.js 整合流程

    Spring Boot 與 Vue.js 整合流程

    本文重點(diǎn)介紹我在Spring Boot 與 Vue.js 整合實(shí)踐過(guò)程中的基本流程,以及遇到的問(wèn)題,感興趣的朋友跟隨小編一起看看吧
    2018-09-09
  • 如何基于springcloud模擬RPC調(diào)用(Feign)

    如何基于springcloud模擬RPC調(diào)用(Feign)

    這篇文章主要介紹了如何基于springcloud模擬RPC調(diào)用(Feign),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Spring?Cloud?Alibaba使用Nacos作為注冊(cè)中心和配置中心

    Spring?Cloud?Alibaba使用Nacos作為注冊(cè)中心和配置中心

    這篇文章主要為大家介紹了Spring?Cloud?Alibaba使用Nacos作為注冊(cè)中心和配置中心的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06

最新評(píng)論

北流市| 石台县| 绥棱县| 西乌珠穆沁旗| 南澳县| 潍坊市| 郁南县| 土默特左旗| 昌黎县| 亚东县| 城固县| 锦州市| 安溪县| 南岸区| 仲巴县| 仙游县| 威远县| 陵水| 湖口县| 湘西| 兴安盟| 乐清市| 江华| 唐海县| 尉犁县| 黄大仙区| 全州县| 天津市| 枝江市| 西乡县| 怀仁县| 吴桥县| 建湖县| 防城港市| 平乡县| 江北区| 锡林浩特市| 大邑县| 芦溪县| 江津市| 垫江县|