jdk源碼分析ArrayDeque

来源:http://www.cnblogs.com/wewill/archive/2016/10/27/6005340.html
-Advertisement-
Play Games

ArrayDeque 數組迴圈隊列,這個數據結構設計的挺有意思的。 據說此類很可能在用作堆棧時快於 Stack,在用作隊列時快於 LinkedList。 一、容量 1.1預設容量是8=2^3 1.2指定初始化容容量 此方法是給數組分配初始容量,初始容量並不是numElements,而是大於指定長度的 ...


ArrayDeque

數組迴圈隊列,這個數據結構設計的挺有意思的。 據說此類很可能在用作堆棧時快於 Stack,在用作隊列時快於 LinkedList。

一、容量

1.1預設容量是8=2^3

1.2指定初始化容容量

    public ArrayDeque(int numElements) {
        allocateElements(numElements);
    }
    private void allocateElements(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
        }
        elements = new Object[initialCapacity];
    }
此方法是給數組分配初始容量,初始容量並不是numElements,而是大於指定長度的最小的2的冪正數 所以ArrayDeque的容量一定是2的冪整數 計算的方法是用或運算

1.3或運算的特點:

得到的結果大於等於任意一個操作數 結果有趨向每個位都為1的趨勢 所以這樣運算下來,運算得到的結果的二進位一定是每個位都是1,再加一個,就剛好是2的整數冪了

1.4擴展容量

當頭尾指針相遇,則數組存滿了 clipboard 此時要擴展容量,會調用
    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;//bit count faster
        if (newCapacity < 0)
            throw new IllegalStateException("Sorry, deque too big");
        Object[] a = new Object[newCapacity];
        System.arraycopy(elements, p, a, 0, r);//copy the right of head(include head)
        System.arraycopy(elements, 0, a, r, p);//copy the left of the tail(exclude tail)
        elements = a;
        head = 0;
        tail = n;
    }

1.5細說doubleCapacity

註意看: 1.求兩本容量,n<<1,使用位運算要快於四則運算,因為這更貼近運算器電路的設計 2.複製原來的數組到目標數組要註意順序! 可以看到,複製分兩次複製進行,第一次複製head指針右邊的元素(包括head指針指向的那個),第二次複製left指針左邊的元素(不包括tail指針指向的那個) 擴展為原來兩本的容量,結果還是2的冪整數 為什麼容量一定要是2的冪整數呢?待會說

二、頭尾指針

clipboard 一開始,頭尾指針都在下標為0的地方,如果向隊頭插入數據,頭指針向左移,向隊尾插入數據,尾指針向右移 tail指針所在的位置,不存儲數據,代表下一次addLast存儲的地方

三、add

隊列提供增加到隊首和隊尾的兩種方法,註意看怎麼處理指針臨界狀態和指針迴圈

3.1addLast

    public void addLast(E e) {
        if (e == null)
            throw new NullPointerException();
        elements[tail] = e;
        if ( (tail = (tail + 1) & (elements.length - 1)) == head)
            doubleCapacity();
    }
移動tail指針,用了與運算 與運算的特點: 結果一定小於等於任意一個操作符的值 與正數進行與運算結果一定為證 當tail+1了之後,超過數組長度,用與運算可以起到迴圈指針的效果,相當於(tail+1%elements.length) 因為elements.length一定是2的整數冪,當-1了之後每一位一定是1,當tile+1超過數組長度的時候,剛好是2的整數冪,則是10***00這種形式,所以與運算了之後,一定等於0

3.2addFirst

    public void addFirst(E e) {
        if (e == null)
            throw new NullPointerException();
        elements[head = (head - 1) & (elements.length - 1)] = e;
        if (head == tail)
            doubleCapacity();
    }
當head-1<0的時候,用與運算可以得到絕對值,並且迴圈指針 因為當head超過數組,head-1剛好是-1,則二進位每個都是1,與運算了之後,一定是111***111的正數,剛好是數組的最後一個位置

四、指針相遇

當tail == head的時候,首尾指針重合,此時隊列已滿,需要擴展隊列,調用doubleCapacity

五、利用空間局部性

在方法中需要多次調用的全局變數,最好創建一個局部變數來訪問 因為全局變數是在靜態存儲區中的,局部變數是放在棧中的,和方法的指令中同一個區域,所以訪問會更快,提高程式性能 就像下麵這樣
    public boolean removeFirstOccurrence(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)) {
                delete(i);
                return true;
            }
            i = (i + 1) & mask;
        }
        return false;
    }
創建了一個mask,來存放elements.length-1

6.優化刪除策略

    private boolean delete(int i) {
        checkInvariants();
        final Object[] elements = this.elements;
        final int mask = elements.length - 1;
        final int h = head;
        final int t = tail;
        final int front = (i - h) & mask;
        final int back  = (t - i) & mask;

        // Invariant: head <= i < tail mod circularity if (front >= ((t - h) & mask))
            throw new ConcurrentModificationException();

        // Optimize for least element motion
        //最優化刪除策略
        if (front < back) {//如果要刪除的元素在前半段
            if (h <= i) {//如果head在要刪除元素的前面
                System.arraycopy(elements, h, elements, h + 1, front);//將要刪除元素的前繼元素往後移動一格
            } else { // Wrap around
                System.arraycopy(elements, 0, elements, 1, i);//把i前面的元素往後挪一格
                elements[0] = elements[mask];
                System.arraycopy(elements, h, elements, h + 1, mask - h);//head-數組末端(不包括數組末端) 都往後移一格
            }
            elements[h] = null;//幫助垃圾收集
            head = (h + 1) & mask;//頭指針回退一格
            return false;
        } else {
            if (i < t) { // Copy the null tail as well
                System.arraycopy(elements, i + 1, elements, i, back);
                tail = t - 1;
            } else { // Wrap around
                System.arraycopy(elements, i + 1, elements, i, mask - i);
                elements[mask] = elements[0];
                System.arraycopy(elements, 1, elements, 0, t);
                tail = (t - 1) & mask;
            }
            return true;
        }
    }
這段源碼我覺得很值得一看,他用了最優刪除策略,都適用與數組實現的數據結構 當刪除的時候,先計算刪除點是在隊列的上半段還是下半段 如果是上半段,則上半段移動一格 這樣子可以達到最少的元素移動! 對於這種迴圈隊列,需要註意刪除元素的位置,有兩種特殊位置

6.1第一種特殊位置

clipboard1 此時刪除節點在隊列的上半段,但是上半段是斷開的 這個時候要移動上半段的話,要分兩次移動 第一次移動刪除元素前面的,第二次移動頭指針到數組末端

6.2第二種特殊位置

clipboard



查看原文:http://blog.zswlib.com/2016/10/27/jdk%e6%ba%90%e7%a0%81%e5%88%86%e6%9e%90arraydeque/


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 主域控角色遷移和奪取(轉載) 轉載自:http://yupeizhi.blog.51cto.com/3157367/1427978 操作系統版本:Windows2012R2 數據中心版 FSMO角色遷移 主域控沒有掛的情況下使用FSMO角色遷移 FSMO角色遷移步驟,下麵步驟在備用域控上執行 0、使 ...
  • Permission deny 許可權 拒絕 查看許可權 ls -a ls -la expression 查看文件夾裡邊東西的許可權 用戶群的分類 組群:一個操作系統可能幾個人同時用 方便小組的文件安全和共用 用戶 組群(在一個組裡邊的) Others root rws rws rws 天神 使用者/ro ...
  • ...
  • 首先聲明,代碼是從一個大神的源碼里偷來的,我稍微整理了一下,現在可以通用。 作用是把你現有的資料庫表結構和數據生成yii2的遷移文件 1.下載模塊源碼解壓縮到 backend/modules/ 2.在 backend/config/main.php 添加如下配置 3.在你的後臺訪問 模塊下載地址 : ...
  • 創建線程的第一種方式: 這種方式的特點(缺陷):線程任務和線程是綁定在一起的。 示例: 四個視窗同時賣票 因為是同時,所以使用多線程。 創建四個線程,都是賣票。 因為都是賣票,所以四個線程的任務是一樣的。 只需要定義一個類繼承Thread。 為瞭解決四個線程共用票的問題,需要使用創建線程的第二種方式 ...
  • 如果對什麼是線程、什麼是進程仍存有疑惑,請先Google之,因為這兩個概念不在本文的範圍之內。 用多線程只有一個目的,那就是更好的利用cpu的資源,因為所有的多線程代碼都可以用單線程來實現。說這個話其實只有一半對,因為反應“多角色”的程式代碼,最起碼每個角色要給他一個線程吧,否則連實際場景都無法模擬 ...
  • Java類庫里有四個表示流的抽象類:InputStream、OutputStream、Reader、Writer。 其中 InputStream 和 OutputStream 是對位元組進行操作的輸入流和輸出流;Reader 和 Writer 是對字元操作的輸入輸出流。 它們是抽象類,在用它們的時候必 ...
  • 英文文檔: format(value[, format_spec]) Convert a value to a “formatted” representation, as controlled by format_spec. The interpretation of format_spec wi ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...