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

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)的 非線程安全的。

因?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ì)代碼支持很差呢!