JDK1 8HashMap底層實現原理

2022-07-03 10:21:08 字數 3418 閱讀 8532

//預設初始容量 2^4 16

//hashmap 底層陣列的長度總是 2 的 n 次方,這一點可參看後面關於 hashmap 構造器的介紹;

//當length 總是 2 的倍數時,h & (length-1)將是乙個非常巧妙的設計:假設 h=5,length=16, 那麼 h & length - 1 將得到 5;

//如果 h=6,length=16, 那麼 h & length - 1 將得到 6 ……如果 h=15,length=16, 那麼 h & length - 1 將得到 15;

//但是當 h=16 時 , length=16 時,那麼 h & length - 1 將得到 0 了;當 h=17 時 , length=16 時

//那麼 h & length - 1 將得到 1 了……這樣保證計算得到的索引值總是位於 table 陣列的索引之內

static final int default_initial_capacity = 1 << 4; // aka 16

//容量最大值 2^30

static final int maximum_capacity = 1 << 30;

//預設載入因子0.75;當表中超過75%的位置已經填入元素,這個表就會用雙倍的桶數自動地進行;結合時間和空間效率考慮得到的乙個折中方案

static final float default_load_factor = 0.75f;

//鍊錶轉為紅黑樹的閥值;當桶內鍊錶節點數》8;就會將桶內資料結構從鍊錶轉為紅黑樹

static final int treeify_threshold = 8;

//進行resize;當<6時;紅黑樹轉為鍊錶;

static final int untreeify_threshold = 6;

//最小樹形化容量閥值,為了避免進行擴容、樹形化選擇的衝突

//min_treeify_capacity出現在了treeifybin()中,避免了在treeifybin()前判斷capacity與min_treeify_capacity比較的**量過大

static final int min_treeify_capacity = 64;

//在第一次使用時初始化node陣列,桶物件陣列

transient node table;

//當被呼叫entryset時被賦值。通過keyset()方法可以得到map key的集合,通過values方法可以得到map value的集合

transient set> entryset;

//該hashmap中共有多少個鍵值對

transient int size;

//當前 hashmap 修改的次數,這個變數用來保證 fail-fast 機制

transient int modcount;

//閾值 下次需要擴容時的值,等於 容量*載入因子;threshold = capacity * load factor

int threshold;

//載入因子,預設得載入因子也是在建構函式裡要賦值給這個得

final float loadfactor;

public hashmap(int initialcapacity, float loadfactor) 

//get方法

總的來講就是

1.判斷表是否為空或者待查詢的桶不為空

2.首先檢查待查詢的桶的第乙個元素是否是要找的元素,如果是直接返回

3.桶內紅黑樹,則呼叫gettreenode()查詢紅黑樹

4.桶內是鍊錶,遍歷鍊錶尋找節點

public v get(object key) while ((e = e.next) != null);}}

return null;

}//put方法

總的來講 就是

1.如果表為空或者表的長度為0,呼叫resize初始化表,為表分配空間

2.二次雜湊處的桶為空,直接插入元素;

3.桶不為空,桶處的第乙個節點與待插入節點的雜湊相同且key「相等」,直接賦給變數e,桶中是紅黑樹,

呼叫puttreeval插入紅黑樹中,桶中是鍊錶,遍歷鍊錶,如果其中存在相同的key,則賦給變數e;不存

在則尾插法加入鍊錶,並判斷節點數是否大於8,如果大於8則呼叫treeifybin()轉化為紅黑樹

4.e不為空,替換其中的value值,並返回舊的value值,e為空,表大小+1,判斷是否達到了閾值,如果達到了則需要擴容

public v put(k key, v value) 

final node resize() 

// 新的閾值是舊的兩倍 double threshold 且容量也為舊的兩倍

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

oldcap >= default_initial_capacity)

newthr = oldthr << 1; // double threshold

}// 如果舊容量<=0 而舊閾值》0,陣列的新容量設定為老陣列擴容的閾值

// 這個應該使用的是帶引數的建構函式(這個已經將閾值弄為2的次方了(使用tablesizefor方法),所以不用擔心容量不為2的次方了)

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

newcap = oldthr;

// 這裡是舊容量<=0 且舊閾值<=0 舊閾值<=0,代表使用的預設不帶引數的建構函式 // 第一次初始化雜湊表的操作到oldtab != null之前

else

if (newthr == 0)

// 這裡回答了為什麼閾值之前不是容量和載入因子的乘積了,在resize初始化table時會重新賦值

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;

}小弟不才,這是hashmap 訪問值的部分原理剖析;後續有補充再加上

JDK 1 8 HashMap擴容原理

擴容前計算索引 1010 0101 0000 1111 0000 0101 索引結果 5擴容以後容量是n 32 對應的二進位制是0001 1111 node本身的hash值是不變的,仍然是1010 0101,那麼擴容後node 的索引的計算是通過如下方式得到 擴容後計算索引 1010 0101 00...

JDK1 8 HashMap原始碼解析

普通常量 儲存node鍊錶的陣列 transient node table 由node節點構成的set集合 transient set entryset hashmap儲存元素的數量 transient int size 記錄hashmap結構性變化的次數 value覆蓋不算 和fail fast機...

JDK 1 8 HashMap原始碼解析

put方法分析 public v put k key,v value hash方法解析 減少hash衝突 static final int hash object key putval方法具體實現 final v putval int hash,k key,v value,boolean onlyi...