JDK原始碼學習01 HashMap原始碼學習

2021-10-10 07:31:29 字數 4554 閱讀 1039

hashmap中直接注意的細節:

紅黑樹長度小於閾值(yu 4聲)6轉化成兩邊

鍊錶長度大於閾值8 且table的長度大於等於64 才樹化 僅滿足鍊錶長度大於閾值8只會呼叫resize擴容

為什麼是6和8呢,而不設定成一樣呢,因為為了防止在邊界反覆橫跳,浪費效能
if (tab == null || (n = tab.length) < min_treeify_capacity)

resize();

hashmap擴容時資料遷移

a. 舊表槽為空 直接跳過

b. 舊表槽中僅乙個節點 求得新的索引再放入

newtab[e.hash & (newcap - 1)] = e;

c. 舊表中是紅黑樹

紅黑樹中其實依舊維持了鍊錶結構 故處理方法和煉標一樣

d. 舊表中是煉標

首先  e.hash是不變的  這裡設oldcap老表長度為16

則老表的下標計算只是用了e.hash的低4位 已知e.hash&15=j

由於表擴容了一倍 所以現在 e.hash的低5位都應該被使用

而已經有了底4位使用的記錄 如果e.hash的第五位是0 則資料所在位置和老表一樣

如果e.hash的第五位是1 則位置為j+oldcap即j+newtab.length/2

因為如果e.hash的第五位是1 那麼資料必然會儲存在新錶的後半段

而低四位的值就相當於offset這裡是j 就就是儲存位置相對於newtab.length/2的偏差

# 預設初始化容量,在第一次put時table才會初始化為該容量

# 如果new hashmap(n) 指定了長度 則直接table為該容量

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

# table最大容量2^30次方

static final int maximum_capacity = 1 << 30;

# 載入因子 當size>default_load_factor*capacity時會擴容

static final float default_load_factor = 0.75f;

# 樹化閾值 鍊錶長度大於閾值8 且table的長度大於64才樹化 僅滿足鍊錶長度大於閾值8只會呼叫resize擴容

static final int treeify_threshold = 8;//大於8 切table長度大於64

# 最小樹化容量 必須同時滿足鍊錶大於8 且長度大於64才能樹化 只滿足大於8就會擴容

static final int min_treeify_capacity = 64;

# 取消樹化閾值 當樹的長度小於6的時候 會退化成煉表

static final int untreeify_threshold = 6;//小於6

# hashmap的雜湊表

transient node table;

# 儲存快取的entryset()。請注意,抽象對映字段用於keyset()和values()。

transient set> entryset;

# 當前大小

transient int size;

# 表結構修改次數 此欄位用於使hashmap集合檢視上的迭代器快速失敗

transient int modcount;

# 下次需要擴容的大小 (capacity * load factor). 當size>threshold且table長度大於64才擴容

int threshold;

# 實際的載入因子 可以在new hashmap(n,loadfactor)的時候指定

final float loadfactor;

public hashmap(int initialcapacity, float loadfactor)

this.loadfactor = loadfactor;

this.threshold = tablesizefor(initialcapacity);

hashcode生成的是32位的碼,為了完整使用到32位,並更好的雜湊,就將高16位與低16位異或(如果相同則為0,如果不同則為1)

【原因:使用 | 會出現更多1 使用 & 會出現更多0 故都不合適】

static

final

inthash

(object key)

這裡我們使用乙個demo來測試一下hash函式,我們可以發現,hash是在hashcode的基礎上,把高16位保持不變,第十六位變成【高十六位和低16位異或】,保證了在不太大的容量的情況下更好的雜湊性

public

static

void

main

(string[

] args)

輸出:

692404036

692393473

101001010001010011111101000100

101001010001010001011000000001

tablesizefor的功能(不考慮大於最大容量的情況)是返回大於輸入引數且最近的2的整數次冪的數。

static

final

inttablesizefor

(int cap)

通過put函式 我們能很好的認識到hashmap的結構

final v putval

(int hash, k key, v value,

boolean onlyifabsent,

boolean evict)

//如果e和當前節點的key相等 則break e記錄舊節點的值

if(e.hash == hash &&

((k = e.key)

== key ||

(key != null && key.

equals

(k))))

break

; p = e;}}

//如果存在舊節點 返回舊值 並更新舊節點的值

if(e != null)

}++modcount;

//大於負載因子*容量的時候才擴容 故初始時put第13個擴容if(

++size > threshold)

//the next size value at which to resize (capacity * load factor).

resize()

;afternodeinsertion

(evict)

;return null;

}

final node

resize()

//newcap=2*oldcap 擴容到原來的兩倍大小

elseif(

(newcap = oldcap <<1)

< maximum_capacity &&

oldcap >= default_initial_capacity)

newthr = oldthr <<1;

// double threshold

}else

if(oldthr >0)

// initial capacity was placed in threshold

//當使用new hashtable(n)給定了長度 n先被轉化成2的冪 然後new node[newcap]出table的長度

newcap = oldthr;

else

//設定下次擴容的閾值capacity*loadfactor

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;

}

JDK原始碼學習 HashMap

什麼是hash?hash的意思是 雜湊 音譯做 雜湊 輸入乙個任意長度的資料,進過雜湊運算之後,輸出一段固定長度的資料,作為輸入資料的指紋,輸出的結果就是雜湊值。一般來說輸入資料的空間遠遠大於輸出的雜湊值的空間,輸入不同的資料可能會產生相同的雜湊值,所以很難從雜湊值來逆向推出輸入值是什麼。雜湊函式本...

JDK原始碼學習 HashMap(一)

public class hashmapextends abstractmapimplements map,cloneable serializable 類定義 hashmap繼承了abstractmap,實現map,cloneable,serializable 介面 abstractmap 實現了...

JDK原始碼學習之集合

collection介面所有集合類的最頂層介面。從網上找到了乙個uml類圖感覺不錯。接下來會仔細閱讀arraylist,linkedlist,hashset,treeset,甚至更多。先分析這幾個的原因,主要是因為比較常用。1.int size 返回集合中的元素總數 2.boolean isempt...