HashMap原始碼解析《一》put方法

2021-08-17 17:27:51 字數 4814 閱讀 2753

在jdk1.8裡邊 hashmap的實現原理不僅僅只由位桶+鍊錶實現 ,增加位桶+紅黑樹實現,在桶中鍊錶儲存的元素個數到達閾值時(預設為8),將在底層將鍊錶轉為紅黑樹,但因為我本人紅黑樹掌握的並不好,所以不討論有關紅黑樹,本文主要講解基礎,hashmap的put方法,其他請等待下文;

一,所涉及到的名詞;

1,bucket   桶,對於系統開始初始化hashmap的時候,會建立乙個長度為capacity的陣列,陣列裡邊儲存元素的位置被稱為桶,儲存乙個node

2,capacity    容量  capacity就是指hashmap中桶的數量,預設初始值為16,必須為2的冪

3,size  表示hashmap中存在kv的數量(陣列和鍊錶的總和),如果size到達threshold極限值會進行擴充。

4,loadfactor 負載因子(裝載因子) ,裝載因子用來平衡hashmap滿的程度,預設值為0.75f.計算hashmap的實時裝載因子的方法為:size/capacity,而不是用已占用桶的數量去初以capacity  滿的程度就是當前的桶中被占用了多少。  例如 現在capacity=16,占用了乙個桶size = 1, 1/16 = 0.0625     就是表示現在有多滿。

5,threshold   極限值    size的值到達極限值之後就會進行擴充。

threshold=capacity*loadfactor         例如現在capacity=16, loadfactor = 0.75   16*0.75 = 12  意思就是當現在桶和鍊錶中的bin總量等於12 時,就會擴充

二,原始碼分析:

1.初始化:

hashmap 繼承於abstractmap,實現了map,cloneable,serializable介面

hashmap是無序的,有序的有treemap,linkedhashmap等

靜態成員定義:

static final int default_initial_capacity = 1<<4;    //初始值為16,並且必須是2的冪

static final int maximum_capacity = 1<<30;            //最大容量 2^30

static final float defalt_load_factor = 0.75f;       //負載因子

static final int treeify_threshold = 8;             //由鍊錶轉為數的極限值

static final int untreeify_threshold = 6;          //由樹轉為鍊錶的閾值

static final int min_treeify_capacity = 64;         

//當桶中的資料被樹化時最小的hash表容量(如果沒有達到這個閾值,即hash表容量小於min_treeify_capacity,當桶中bin的數量太多時會執行resize擴容操作)這個min_treeify_capacity的值至少是treeify_threshold的4倍。

桶中的每個資料是node的例項,其中包含node(int hash,k key,v value, nodenext);  也就是說桶中的元素所包含這些屬性

node 是 hashmap中的乙個內部靜態類,他的初始定義如下:

他的內部重寫了equals和hashcode方法

static class nodeimplements map.entry

public final k getkey()

public final v getvalue()

public final string tostring()

public final int hashcode()

public final v setvalue(v newvalue)

public final boolean equals(object o)

return false;}}

我們一般使用

hashmap 

例項化使用無參的構造方法,

只是初始化和負載因子

還有其他三種種構造方法,分別為其指定桶的大小,桶的大小和負載因子,將乙個新的map填充進去

構造方法在下一章講解  一併講解tablesizefor方法

2.put

方法(重點)

hashmap中我們用的最多的就是

put,get

方法了。

在這之前我們先了解一下put方法所相關的兩個方法

hashmap在初始化的時候不會去初始化node  table   只有在你往map中新增值的時候他才回去判斷table有沒有被初始化,沒有則去初始化,並設定預設大小;

初始化: 紅色 注釋是初始化過程要執行的

(1).有關桶的初始化和size的擴充()

final node resize() 

//先讓新的容量等於舊容量二倍 之後新容量沒到達最大值, 並且之前容量大於等於初始化大小則將閾值擴大二倍

else if ((newcap = oldcap << 1) < maximum_capacity &&

oldcap >= default_initial_capacity)

newthr = oldthr << 1; // double threshold

} else if (oldthr > 0) // initial capacity was placed in threshold

//如果舊的閾值大於0, 則賦值給新的容量 在這裡操作的前提是在初始化hashmap時呼叫的是帶參的構造方法,

//設定過閾值,但是閾值實際是容量值 詳情請看在帶參初始化時呼叫tablesizefor方法

newcap = oldthr;

else

if (newthr == 0)

threshold = newthr;

@suppresswarnings()

//申請新大小的桶陣列

node newtab = (node)new node[newcap];

//並且將新的空間賦值給原始桶陣列

table = newtab;

if (oldtab != null) else

} while ((e = next) != null);

if (lotail != null)

if (hitail != null)

} } }

} //返回新陣列

return newtab;

}

(2).hashmap中hash值獲取方式

static final int hash(object key)
// 我們在

put的時候,傳入的是

string

型別的鍵值,存放的就是

string

型別嗎?

我們一步一步深入解析

public v put(k key, v value)
這是put()方法的原始碼,主要操作在putval中,

put方法的執行過程大致是這樣的,取得鍵值,並取得到鍵的hash值,根據hash&(陣列長度-1)作為索引尋找桶的位置,然後檢測該索引下是否存在值,沒有,將node(前邊有相應的構造方法)型別的值加入引數已注入,若有,先判斷他們的hash值是否一致(不同的hash值&相同值可能存在 結果一致,即索引一致)並且鍵值是否一致,若都一致,則覆蓋對應的位置node裡邊的值,並將原來的值返回,若不相等,遍歷鍊錶,若一直沒有滿足要求的值,則將當前node儲存在當前位置的桶中,並且將指標指向原來鍊錶的首部。

//如果找到鍵值一樣的,停止鍊錶遍歷

if (e.hash == hash &&

((k = e.key) == key || (key != null && key.equals(k))))

break;

//沒找完且沒找到,則繼續找,然當前引用指向下乙個引用(c 語言鍊錶的遍歷)

p = e;}}

//如果桶陣列或者鍊錶中存在和當前的node鍵相等的

v oldvalue = e.value;

//如果允許覆蓋值,或者之前的值為空

if (!onlyifabsent || oldvalue == null)

//覆蓋

e.value = value;

//供linkedhahmap繼承使用的

afternodeaccess(e);

//返回舊值

return oldvalue;}}

// modcount字面意思就是修改次數

++modcount;

//即不存在碰撞 將node放入桶表中 

//如果當前桶陣列的size達到閾值,擴充套件

if (++size > threshold)

resize();

//供linkedhahmap繼承使用的 

afternodeinsertion(evict);

return null;

}

HashMap原始碼解析

以jdk1.8為例,hashmap是乙個用於儲存key value鍵值對的集合,每乙個鍵值對是乙個node jdk1.7叫做entry 後台是用乙個node陣列來存放資料,這個node陣列就是hashmap的主幹。這裡我們主要來分析hashmap的get和put方法。public v put k k...

hashMap 原始碼解析

這幾天跳槽 被人問得最多的問題就是基礎方面的知識.當時學習的時候有點囫圇吞棗.現在回頭把這些基本的集合類原始碼都仔細閱讀下 hashmap 用的是最頻繁的.所以問得也最多了.initcapacity 初始化的容量 loadfacotr 負載因子 主要用來計算threshold的值 threshold...

HashMap原始碼解析

預設字段 static final int default initial capacity 1 4 預設node的陣列長度 16 static final int maximum capacity 1 30 陣列的最大長度 2 30 static final float default load ...