JDK原始碼分析 Collections

2022-05-12 17:13:16 字數 3189 閱讀 7032

1. 集合框架圖

hashmap是通過"拉鍊法"實現的雜湊表。它包括幾個重要的成員變數:table, size, threshold, loadfactor, modcount。

table是乙個entry陣列型別,而entry實際上就是乙個單向鍊錶。雜湊表的"key-value鍵值對"都是儲存在entry陣列中的。 

size是hashmap的大小,它是hashmap儲存的鍵值對的數量。 

threshold是hashmap的閾值,用於判斷是否需要調整hashmap的容量。threshold的值="容量*載入因子",當hashmap中儲存資料的數量達到threshold時,就需要將hashmap的容量加倍。

loadfactor就是載入因子。 

modcount是用來實現fail-fast機制的。

static

class entryimplements map.entry

hashmap是根據這個方法定位到某個元素,即table陣列的哪個位置。

static

int indexfor(int h, int

length)

就一行**,將key的二次hash值,與長度減一進行與操作,這一步可謂經典,通常我們會用取模的方式來定位陣列中的某個位置,我們首先想到的就是把hashcode對陣列長度取模運算,這樣一來,元素的分布相對來說是比較均勻的。但是,「模」運算的消耗還是比較大的,能不能找一種更快速,消耗更小的方式那?

hashmap用這種方法,而且length即capacity的值,而capacity又是2的倍數,減1之後,表示成二進位制就全部是1了,那麼與全部為1的乙個數進行與操作,速度會大大提公升了。這就是為什麼"capacity的值是2的倍數"

當hashmap中的元素越來越多的時候,碰撞的機率也就越來越高(因為陣列的長度是固定的),所以為了提高查詢的效率,就要對hashmap的陣列進行擴容,陣列擴容這個操作也會出現在arraylist中,所以這是乙個通用的操作。而在hashmap陣列擴容之後,最消耗效能的點就出現了:原陣列中的資料必須重新計算其在新陣列中的位置,並放進去,這就是resize。

那麼hashmap什麼時候進行擴容呢?當hashmap中的元素個數超過陣列大小*loadfactor時,就會進行陣列擴容,loadfactor的預設值為0.75,也就是說,預設情況下,陣列大小為16,那麼當hashmap中元素個數(

不是陣列長度

)超過16*0.75=12的時候,就把陣列的大小擴充套件為2*16=32,即擴大一倍,然後重新計算每個元素在陣列中的位置,而這是乙個非常消耗效能的操作,所以如果我們已經預知hashmap中元素的個數,那麼預設元素的個數能夠有效的提高hashmap的效能。比如說,我們有1000個元素new hashmap(1000), 但是理論上來講new hashmap(1024)更合適,不過即使是1000,hashmap也自動會將其設定為1024。 但是new hashmap(1024)還不是更合適的,因為0.75*1000 < 1000, 也就是說要裝滿1000元素, 我們必須這樣new hashmap(2048)才最合適,既考慮了&的問題,也避免了resize的問題。 

private

void inflatetable(int

tosize)

void resize(int

newcapacity)

entry newtable = new

entry[newcapacity];

transfer(newtable, inithashseedasneeded(newcapacity));

table =newtable;

threshold = (int)math.min(newcapacity * loadfactor, maximum_capacity + 1);

}

/**

* transfers all entries from current table to newtable.

*/void transfer(entry newtable, boolean

rehash)

int i =indexfor(e.hash, newcapacity);

e.next

=newtable[i];

newtable[i] =e;

e =next;}}

}

這裡使用了單鏈表的頭插入方式,同一位置上新元素總會被放在鍊錶的頭部位置,如下圖所示:

treemap的本質是r-b tree(紅黑樹),它包含幾個重要的成員變數: root, size, comparator。

root 是紅黑數的根節點。它是entry型別,entry是紅黑數的節點,它包含了紅黑數的6個基本組成成分:key(鍵)、value(值)、left(左孩子)、right(右孩子)、parent(父節點)、color(顏色)。entry節點根據key進行排序,entry節點包含的內容為value。 

紅黑數排序時,根據entry中的key進行排序;entry中的key比較大小是根據比較器comparator來進行判斷的。

size是紅黑數中節點的個數。

static

final

class entryimplements map.entry

4. weakhashmap

weakhashmap的鍵是「弱鍵」。在 weakhashmap 中,當某個鍵不再正常使用時,會被從weakhashmap中被自動移除。更精確地說,對於乙個給定的鍵,其對映的存在並不阻止垃圾**器對該鍵的丟棄,這就使該鍵成為可終止的,被終止,然後被**。某個鍵被終止時,它對應的鍵值對也就從對映中有效地移除了。

這個「弱鍵」的原理呢?大致上就是,通過weakreference和referencequeue實現的。 weakhashmap的key是「弱鍵」,即是weakreference型別的;referencequeue是乙個佇列,它會儲存被gc**的「弱鍵」。實現步驟是:

與hashmap一樣,weakhashmap是不同步的。

5. linkedhashmap

JDK原始碼分析 Vector

vector和arraylist有一定的不同 int newcapacity oldcapacity capacityincrement 0 capacityincrement oldcapacity 從這一句我們可以看出 增長容量 capacityincrement 0 增長倍數 陣列數量 舊容量...

jdk原始碼分析之 HashTable

hashtable整體架構 hashtable實現雜湊表,該雜湊表將鍵對映到值。可以是任何不為空的key或者value。主要是因為hashtable的儲存或者檢索key必須實現hashcode 和equals方法,其類圖如下 類註解資訊 重要的成員變數 存放雜湊表的資料 private transi...

分析HashMap 的 JDK 原始碼

緣由 今天好友拿著下面的 問我為什麼 map.entry 這個介面沒有實現 getkey 和 getvalue 方法,卻可以使用,由此,開啟了一番查閱 jdk 原始碼的旅途 map map new hashmap map.put 1,張三 map.put 2,李四 map.put 3,王五 map....