HashMap1 8原始碼分析

2021-08-31 11:43:47 字數 3144 閱讀 8641

1:hashmap的原理,內部資料結構如何?

底層使用雜湊表(陣列+鍊錶),當鍊表過長(其實是大於8)的時候會將鍊錶轉換成紅黑樹,以實現n log(n) 的查詢。

2:具體過程

對 key 求 hash 值,然後再計算 下標。

如果沒有碰撞,直接放入桶中,

如果碰撞了,以鍊錶的方式鏈結到後面,

如果鍊錶長度超過閥值(treeify_threshold == 8),就把鍊錶轉成紅黑樹。

如果節點已經存在就替換舊值

如果桶滿了(容量 * 載入因子),就需要 resize。

3 : 原始碼分析

putval(hash(key), key, value, false, true);

//hash()方法

static final int hash(object key)

為什麼不直接返回key.hashcode()---這是由jvm決定的,就是key的位址;而是返回key的hashcode 該值的高16位與其低16位異或後的值呢

首先,h是32位的,而採用以後運算得到的0和1是均勻的,因為我們應該盡量使這個值不同,& | 運算都會偏向0或1,如下圖所示

putval(.......)方法原始碼分析

final v putval(int hash, k key, v value, boolean onlyifabsent,

boolean evict)

if (e.hash == hash &&

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

break; //這種情況會執行下面的if(e != null)

p = e;}}

v oldvalue = e.value;

if (!onlyifabsent || oldvalue == null)

e.value = value;

afternodeaccess(e);

return oldvalue;}}

++modcount;

if (++size > threshold) //這個threshold就是用下判斷是否需要擴容的

resize();

afternodeinsertion(evict);

return null;

}

4:問題hashmap 中 hash 函式怎麼是是實現的?

高 16bit 不變,低 16bit 和高 16bit 做了乙個異或

(n - 1) & hash --> 得到下標

陣列每個位置上的鍊錶長度為什麼要限制長度呢?

如果在某個位置上發生過多hash碰撞,在put的時候就會發生順延,要從頭到尾比遍歷這個鍊錶,效率低(put的時候是尾插法),所以在jdk1.8的時候長度超過8的時候就轉為紅黑樹。

怎麼減少hash碰撞,盡可能的使陣列位置都用到?

i = (n-1) & hash,所以是由n 和 hash控制的,顯然我們能夠操作的是hash,就是通過key的hashcode的高16位和低16異或。

為什麼n一定要是n的2次冪呢?

其實就是為了保證在計算i的時候,在&操作的時候,(n -1)獲的每一位都為1,此時i的值才取決有hash。

hashmap 怎樣解決衝突,講一下擴容過程,假如乙個值在原陣列中,現在移動了新陣列,位置肯定改變了,那是什麼定位到在這個值新陣列中的位置(可看下面擴容原始碼分析解釋)

將新節點加到鍊錶後,

容量擴充為原來的兩倍,然後對每個節點重新計算雜湊值。

這個值只可能在兩個地方,乙個是原下標的位置,另一種是在下標為 《原下標+原容量》 的位置。

幾個引數意思

//陣列預設的容量:1左移4位  16

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

//最大容量 2的30次冪

static final int maximum_capacity = 1 << 30;

//載入因子,用來乘以容量,算出乙個判斷擴容的數值

static final float default_load_factor = 0.75f;

//鍊錶長度超過這個長度後轉為紅黑樹

static final int treeify_threshold = 8;

5:擴容原始碼分析:雙倍擴容(保證容量為2的n次冪)

final node resize() 

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

oldcap >= default_initial_capacity)

newthr = oldthr << 1; // 擴容後,這個閾值也要翻倍

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

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;

}

原始碼分析 HashMap 1 8

1.0 資料結構 2.0 儲存流程 3.0 陣列元素 鍊錶節點的實現類 hashmap中的陣列元素 鍊錶節點 採用node類 實現,與jdk1.7相比只是把entry換了個名字 hashmap中的紅黑樹節點 採用treenode類 實現 紅黑樹節點 實現類 繼承自linkedhashmap.entr...

HashMap 1 8 原始碼閱讀

一 初始化 1.無參建構函式 負載因子預設值 static final float default load factor 0.75f 指定loadfactor負載因子的值是0.75f public hashmap 2.指定初始化大小和負載因子 hashmap的最大容量 static final i...

JDK原始碼分析系列 HashMap 1 8

預設的初始化容量,必須是2的n次冪 static final int default initial capacity 1 4 aka 16 最大的容量是2的30次冪 static final int maximum capacity 1 30 預設的負載因子 static final float ...