jdk1.8--HashMap簡(jiǎn)單筆記

一、概述

1.1 jdk_1.8之前HashMap都是基于數(shù)組+鏈表實(shí)現(xiàn) 非線程安全的。

1.1

1.2 缺點(diǎn):如果出現(xiàn)hash碰撞(桶entry碰撞),就會(huì)退化成單鏈表!查找時(shí)間從O(1)到O(n)。最好不要出現(xiàn)hash碰撞。

2.1 jdk_1.8之前后HashMap都是基于數(shù)組+鏈表+紅黑樹實(shí)現(xiàn)的 非線程安全的。


2.1

因?yàn)閖dk_1.8之前出現(xiàn)桶碰撞, 在鏈表中查找數(shù)據(jù)會(huì)出現(xiàn)很大的性能損失。所以jdk_1.8之后當(dāng)出現(xiàn)hash值沖突時(shí), 如果鏈表node節(jié)點(diǎn)大于8時(shí)不再采用鏈表,轉(zhuǎn)而使用紅黑樹代替鏈表!提高查找效率。

關(guān)于性能提升參考:http://blog.csdn.net/lc0817/article/details/48213435/



二、源碼解析

1 字段解析(注釋來(lái)自百度翻譯? 勿怪)

/**

*表中,第一次使用初始化,并調(diào)整其大小為

*必要。分配時(shí),長(zhǎng)度總是兩個(gè)冪。

*我們也允許在某些操作中允許長(zhǎng)度為零

*目前不需要的自舉機(jī)構(gòu)。)

*/

transient Node[]table;? 這個(gè)就是上圖中的 hash數(shù)組。



/ * *

*此映射中包含的鍵值映射的數(shù)量。

* /

transient int size; 個(gè)人覺得就是Node<k,v>節(jié)點(diǎn)數(shù)量

/ * *

*調(diào)整大小的下一個(gè)尺寸值(容量*負(fù)載因子)。

*

* @系列

* /

/ /(javadoc的描述是真實(shí)的在序列化。此外,如果尚未分配表數(shù)組,則字段保留初始數(shù)組容量,或零表示 default_initial_capacity。)

int?threshold;? 個(gè)人理解為擴(kuò)容閥值 如果 size(node節(jié)點(diǎn)) 大于這個(gè)值是 則對(duì)table數(shù)組進(jìn)行擴(kuò)容。

float?loadFactor;? 裝載因子

static?final?int?DEFAULT_INITIAL_CAPACITY?=?1?<<?4; 默認(rèn)初始化數(shù)組大小為16

static?final?float?DEFAULT_LOAD_FACTOR?=?0.75f;?? 默認(rèn)裝載因

/ * *

*使用樹的bin計(jì)數(shù)閾值,而不是列表

*倉(cāng)。當(dāng)添加一個(gè)元素到一個(gè)

* bin至少有這么多節(jié)點(diǎn)。值必須更大。

*大于2,至少應(yīng)符合假設(shè)的8

*樹搬遷轉(zhuǎn)換回平原垃圾桶后

*收縮。

* /

static?final?int?TREEIFY_THRESHOLD?=?8; 鏈表最大長(zhǎng)度 大于這個(gè)長(zhǎng)度,鏈表轉(zhuǎn)化為紅黑樹

2. 構(gòu)造函數(shù)

相關(guān)自己可以看下源碼

知道存儲(chǔ)的數(shù)據(jù)大小時(shí)最好指定大小

3. 關(guān)于Node類型

Node<k,v>[] table;

Node<K,V> 是一個(gè)HashMap的一個(gè)靜態(tài)內(nèi)部類。他是實(shí)現(xiàn)于Map.Entry<k,v>

重要參數(shù):

final?int?hash;? hash值?

final?K?key; 存儲(chǔ)的key

V?value;?? 存儲(chǔ)的值

Node<k,v> next;?? 指向的下一個(gè)節(jié)點(diǎn)的地址

4 存儲(chǔ)方法

public V put(K key, V value) {

return putVal(hash(key), key, value, false, true);

}


static final int hash(Object key) {

int h;

?//通過(guò)這里我們也就可以發(fā)現(xiàn)key是可以為null的,如果為空返回0也就是table[0]的位置。

? ? return (key ==null) ?0 : (h = key.hashCode()) ^ (h >>>16);

}


final V putVal(int hash, K key, V value, boolean onlyIfAbsent,

? ? ? ? ? ? ? boolean evict) {

Node[] tab; Node p; int n, i;

//將table賦值給 tab 如果tab為null或者長(zhǎng)度為0 則重新去初始化table(注意在resize里面 由于 現(xiàn)在tab和table 指向的是同一個(gè)地址 所以也是初始化tab)

if ((tab =table) ==null || (n = tab.length) ==0)

???? n = (tab = resize()).length;

//tab[i = (n -1) & hash] 這樣寫還是第一次見, 反正意思通過(guò) (n -1) & hash 得到數(shù)組下標(biāo)的值 為空的時(shí)候 就去創(chuàng)建一個(gè)新的節(jié)點(diǎn)并保存到那個(gè)下標(biāo)((n -1) & hash)上去

if ((p = tab[i = (n -1) & hash]) ==null)

???? tab[i] = newNode(hash, key, value, null);

else {

??? //一旦不為空 所以就hash碰撞了。需要組成 鏈表或者樹。

???? Node e; K k;

?? //如果發(fā)現(xiàn)hash值(下標(biāo)) 和 將要存儲(chǔ)的key 都是一致的, 注意:p是通過(guò)(p = tab[i = (n -1) &?? hash]) 得到的? 將這個(gè)值賦給e,? 后面會(huì)對(duì)e執(zhí)行是否覆蓋value的操作。

? ?? if (p.hash == hash &&((k = p.key) == key || (key !=null && key.equals(k))))

?????????? e = p;

? ?? else if (p instanceof TreeNode)

????????? //判斷 節(jié)點(diǎn)是否是紅黑樹類型 如果是執(zhí)行紅黑插入操作。

?????????? e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);

? ?? else {

???????? //到這里說(shuō)明鏈表存儲(chǔ)的,則對(duì)鏈表進(jìn)行循環(huán)依次向后查找

????????? for (int binCount =0; ; ++binCount) {

??????????????? //將p指向的下一個(gè)節(jié)點(diǎn)賦值給 e 如果為null這是鏈表的最后一個(gè)節(jié)點(diǎn)了 則將創(chuàng)建一個(gè)新節(jié)點(diǎn)賦值給p的下一個(gè)節(jié)點(diǎn)即(next)

??????????????? if ((e = p.next) ==null) {

??????????????????????? p.next = newNode(hash, key, value, null);

//如果沖突節(jié)點(diǎn)達(dá)到8個(gè),調(diào)用treeifyBin(tab,?hash),這個(gè)treeifyBin首先回去判斷當(dāng)前hash表的長(zhǎng)度,如果不足64的話,實(shí)際上就只進(jìn)行resize,擴(kuò)容table,如果已經(jīng)達(dá)到64,那么才會(huì)將沖突項(xiàng)存儲(chǔ)結(jié)構(gòu)改為紅黑樹。??

? ? ? ????????????????? if (binCount >=TREEIFY_THRESHOLD -1)// -1 for 1st

? ? ? ? ? ? ? ? ? ? ? ????????? treeifyBin(tab, hash);

?????????????????????? break;

? ? ? ? ?? }

//當(dāng)e 不等于空 且他的hash值和key等于要存儲(chǔ)的hash值 和key時(shí)則結(jié)束循環(huán) 后面會(huì)判斷是否對(duì)value覆蓋

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

break;

?????????????? //把e賦給p繼續(xù)循環(huán)

? ? ? ? ? ? ? ? p = e;

? ? ? ? ? ? }

}

//如果e不等于空說(shuō)明數(shù)或者鏈表存在或者插入 hash值和key一樣的節(jié)點(diǎn)了, 這里就是進(jìn)行對(duì)value判斷是否用新的值覆蓋以前的值!

if (e !=null) {// existing mapping for key

? ? ? ? ? ? V oldValue = e.value;

?????????? //判斷是否修改已插入節(jié)點(diǎn)的value??

? ? ? ? ? ? if (!onlyIfAbsent || oldValue ==null)

e.value = value;

? ? ? ? ? ? afterNodeAccess(e);

? ? ? ? ? ? return oldValue;

? ? ? ? }

}

//插入新節(jié)點(diǎn)后,hashmap的結(jié)構(gòu)調(diào)整次數(shù)+1

++modCount;

? ? if (++size >threshold) //判斷節(jié)點(diǎn)大小是否大于擴(kuò)容閥值大于就執(zhí)行擴(kuò)容

resize();

? ? afterNodeInsertion(evict);

return null;

}

作為第一次寫文章,大量參考其他文章只是對(duì)部分做了點(diǎn)個(gè)人理解和 詳細(xì)解釋!

不到之處歡迎指正。

發(fā)現(xiàn)對(duì)代碼支持很差呢!

參考:JDK1.8 HashMap源碼分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容