ArrayDeque原始碼分析

2021-07-09 14:51:27 字數 3609 閱讀 1734

1. 體系結構

了解特性,先看**繫結構:

如上所示,知道其支援 序列化,轉殖,迭代器操作,佇列特性。具體實現 除了實現以上介面外,擴充套件abstractcollection 抽象類。

2. 應用場景

大炮打蒼蠅,還是鳥槍打野豬?工具應用要有場景:

arraydeque 為雙端佇列,支援首部,尾部兩端的操作,因此做雙端操作可用於fifo等queue, 做單端操作可做為stack.

然而能做queue的還有linkedlist, vector等,能做stack有stack,還要arraydeque幹嘛?

還是那句話,「應用」是要講究「場景」的:

linkedlist內部實現用node節點鏈結前後元素,模擬c/c++的鍊錶(優點在於中間節點的增刪操作為o(1));

vector方法加著synchronized修飾(同步 將帶來效能的損耗);stack的實現又繼承自vector,問題同上。

arraydeque 的底層實現為單純的陣列操作,所以單從效能上看,arraydeque在優於他們,當然由於沒有做同步處理,所以存在併發問題,需要呼叫方自己保障。

3. 原始碼實現

原始碼實現部分,看上去還是相對親切的(當年大學書本上背爛的鍊錶,堆,佇列實現),又複習了一遍c語言入門的佇列實現。當然jdk中的設計畢竟是巧妙的。

挑幾處分析下:

public

boolean

isempty()

public

intsize()

public e pollfirst()

public e polllast()

先說明下關鍵變數 意義:

head第乙個元素的索引

tail最後乙個元素之後的索引。

如 head指向0,tail指向3,那麼有效元素為索引0,1,2 指向的元素,所以元素個數 size() 為 3(即3-0)。

看下上述實現奇怪的地方,大量如此的操作,為何?

(tail - head) & (elements.length - 1)

(h + 1) & (elements.length - 1)

原因在於 arraydeque在實現雙端佇列時才用為迴圈陣列,存在 tail < head的情況,所以要通過 & (elements.length - 1)操作來修正,使其為正值。

所以我們會想到通過補碼是實現是可以的,然而單憑 &(elements.length - 1) 就實現修正,還是難以讓我們信服的。

假設 tail = 3, head = 6, elements.length=10, 那麼結果也應該是 7啊 (元素的索引為6,7,8,9,0,1,2),而非 -3&10 = 8 啊 (-3 補碼 1111 1101 10補碼 0000 1010 結果 0000 1000 為8)

不思不得其解,再看原始碼,其中有一句話

/**

* the minimum capacity that we』ll use for a newly created deque.

* must be a power of 2.

*/ 所以,一切都明白了,elements.length 即陣列長度是有要求的,必須是2的冪指數,如此一切可解,測試一下elments.length 為16,32等都順利成章通過了,原理也很簡單,elments.length 二進位制位 00100..00,elments.length-1 為 000111…11可以做掩碼了,如此,上述原始碼理解通過。

關於arraydequeue在分配空間,要求必須是 2的冪指數,其中有一段實現:

private

void

allocateelements(int numelements)

elements = (e) new object[initialcapacity];

}

關於此處演算法的出處,個人目前還沒有理解,哪位大俠知道還請告知。但是是否能work,我們還是要驗證一下,一下借用一下他人的方法:

public

class arraydequecapacity

}private

static

intgetcapacity(int numelements)

}

結果:

1 1 2 

2 3 4

3 7 8

4 15 16

5 31 32

6 63 64

7 127 128

8 255 256

9 511 512

10 1023 1024

11 2047 2048

12 4095 4096

13 8191 8192

14 16383 16384

15 32767 32768

16 65535 65536

17 131071 131072

18 262143 262144

19 524287 524288

20 1048575 1048576

21 2097151 2097152

22 4194303 4194304

23 8388607 8388608

24 16777215 16777216

25 33554431 33554432

26 67108863 67108864

27 134217727 134217728

28 268435455 268435456

29 536870911 536870912

30 1073741823 1073741824

31 2147483646 1073741824

從驗證結果來看,對於計算大於等於乙個數的最小2的冪指數運算,這個方法還是工作的很好的。

4. 一些問題

那麼,問題又來了,為什麼分配空間一定要按2的冪指數,我是贊同其他同學觀點的(有其他理由的還請補充):

1. 從記憶體空間分配的角度來講,2的冪指數,更加快記憶體分配

linux記憶體分配管理中的夥伴系統以連續2的冪指數(1,2,4,8,…1024)個頁幀(page frame) 為單位進行空間分配,而頁幀做為標準記憶體分配單元大小4k, 所以才用 2的冪指數為大陣列分配空間是有利於進行記憶體管理的。

2. 邏輯判斷的簡化

如上式:

public

intsize()

如果沒有對 elements.length做要求的話,就要如下判斷來

int

sub = tail - head;

if(sub >= 0)

return sub;

else

關於arraydequeue的應用和實現,大致如此。

ArrayDeque原始碼解析

類的字段中包含 初始容量 頭尾指標,以陣列為底層資料結構 transient object elements transient int head transient int tail private static final int min initial capacity 8 構造器 預設構造器...

JDK之ArrayDeque原始碼解讀 三

目錄 作用 獲取arraydeque的第乙個元素。如果arraydeque為null,丟擲異常。作用 獲取arraydeque的最後乙個元素。如果arraydeque為null,丟擲異常。作用 獲取arraydeque的第乙個元素。與getfirst 不同在於,如果arraydeque為null,則...

spring原始碼分析 spring原始碼分析

1.spring 執行原理 spring 啟動時讀取應用程式提供的 bean 配置資訊,並在 spring 容器中生成乙份相應的 bean 配置登錄檔,然後根據這張登錄檔例項化 bean,裝配好 bean 之間的依賴關係,為上 層應用提供準備就緒的執行環境。二 spring 原始碼分析 1.1spr...