HashMap原理总结
?
來總結(jié)一下HashMap的原理
1.HashMap當(dāng)中有一個內(nèi)部類,它叫Node,然后這個Node呢,它其實是實現(xiàn)了Map.Entry接口,這個接口當(dāng)中有幾個抽象的方法和幾個具體的方法。其中Map.Entry<K,V>是一個泛型的元組。
2.Map.Entry接口中有如下抽象方法:
- getKey()
- getValue()
- setValue()
- hashCode()
- equals
3.Node的私有變量如下:
- hash
- key
- value
- Next node
其中HashMap的核心是hashcode的生成算法,hashCode的生成算法如下:
Objects.hashCode(key) ^ Objects.hashCode(value);它是先通過得到Key和value的hashcode,然后對2個值進(jìn)行異或操作后得到的值。
其中Object.hashCode是一個native的方法。
public native int hashCode();其中Node的equals方法,傳入的對象是object,只有當(dāng)object的類型是map.entry并且,當(dāng)前對象的key和value都和傳入的key,value一致,那樣才會返回相等。
下面的這個方法,是計算hash值的方法。它是通過key去計算,然后把拿到的hashcode和它右移16位的結(jié)果進(jìn)行異或操作,具體回頭再看為什么,我也不知道。
static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}hashMap里面還有一個entrySet的成員變量,它是一個set 的集合,這里面的transient關(guān)鍵字不太懂,回頭再看看。
transient Set<Map.Entry<K,V>> entrySet;HashMap里面有一個非常重要的方法,叫做putVal()方法。這算是里面最核心的一個方法了,弄懂了這個方法,80%的HashMap相關(guān)的知識都能弄懂了
首先是有2個Node的聲明,一個是tab,一個是p.
Node<K,V>[] tab; Node<K,V> p; int n, i;下面我們來解析一下PutVal方法,如果table為空,或者table的長度為0,重置table的長度。
if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;首先是下面的代碼會利用到上面的代碼,n得出了一個結(jié)果,那就是resize()后的結(jié)果,下面的n-1就是“”最后“”一個元素
if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);下面的代碼就是上面的tabl[x]里面的邏輯,這里面用到了按位與的一個運算:為什么要這么做?不知道。
來做一個小小的補(bǔ)充,這里要先復(fù)習(xí)一下按位與的結(jié)果操作,什么時候獲取什么值,如果hash是一個負(fù)數(shù),那又是什么情況呢?
?
(n - 1) & hash我猜想的是,如果“”找到的“”元素為null,那么新建一個node元素。并且這個node元素的next為空。否則執(zhí)行else里面的邏輯。
----------------------------------------我是分割線---------------------------------------------
首先putVal方法會去計算這個key的hash值。
首先我覺得要明白hash算法的真諦,網(wǎng)上找的這句話,說得不錯:要找到散列為同一個值的兩個不同的輸入,在計算上是不可能的,所以數(shù)據(jù)的哈希值可以檢驗數(shù)據(jù)的完整性。
當(dāng)?shù)谝淮芜M(jìn)入putval方法的時候,table是空的,所以肯定要進(jìn)行一個resize操作,不光是table,連threshold都是0,所有的東西都未能初始化的情況下,這個時候,應(yīng)該進(jìn)入如下邏輯:
newCap = DEFAULT_INITIAL_CAPACITY;newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);這個時候,newCapicity的話就變成了初始值16,newThr變成了初始化的Threhold,如果是這種情況下的話,就會新建一個長度為16的Node<K,V>[]數(shù)組,最后返回newTable,注意,resize操作的返回對象。
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];在下面的例子中,hash值是一個非常大的值,換算成2進(jìn)制,它是32位長度的一個2進(jìn)制,用按位與的操作是最快的,因為計算機(jī)內(nèi)部結(jié)構(gòu)就是二進(jìn)制的。
if ((p = tab[i = (n - 1) & hash]) == null)?注意!!!它先填充的是tab[10]的內(nèi)容,也就是說,并沒有從0開始填充,這是違背我們直覺的一件事情。
?
?
然后讓modCount++,
最后,如果負(fù)載因子小于size,那么,hashmap會自動擴(kuò)容。
if (++size > threshold)resize();隨后執(zhí)行afterNodeInsertion方法,這個方法在Hashmap當(dāng)中是一個空的方法,API里面介紹的是為LinkedHashMap所用,所以這里不再做討論。還有注意下,如果是新建的hashMap第一次putval,那么它的返回值為null.
?要注意一點,新建一個HashMap,它并不是獨立存在的,在你把你的key添加進(jìn)去之前,它還會添加非常多的其他的KEY,也就是我們所說的:系統(tǒng)路徑,所以最后得出的結(jié)果就是,如果你是一個新的HashMap,那么,你添加了一個KEY,肯定這里面不止一個KEY。
?大家可以觀察到,當(dāng)SIZE=13的時候,
其實是自動進(jìn)入了resize這個方法的,你看我斷點都進(jìn)來了。這就證明了hashmap的自動擴(kuò)容機(jī)制。
?
?那么為什么會有這么多的Node被添加進(jìn)來呢?原因只有一個,就是我們用idea啟動項目的時候,一些類其實是用到了HashMap的,它優(yōu)于我們調(diào)試的時候進(jìn)入的HashMap,所以剛才大家才會看到那么多的節(jié)點被添加到hashMap當(dāng)中去。
?當(dāng)我把在putVal上的斷點去掉以后,就進(jìn)入了如下代碼塊,驗證了我的猜想。
?
另外還有一個很有趣現(xiàn)象,我用單元測試,新建了一個HashMap,結(jié)果。。。你發(fā)現(xiàn)沒有,jdk里面已經(jīng)填充了4項了,原來,我們認(rèn)為的Hashmap,有多少項,就add多少項的觀點其實是錯誤的!!!
下面我們再來看看如下代碼,傳入的hash和之前的hash進(jìn)行對比,這里面可能大家有一些迷糊,當(dāng)然包括我也看不懂,不過從這里可以獲取一個非常重要的信息,這么做的方式就是為了避免一個hashmap鐘可能出現(xiàn)“相同”的hash對象,我是這么理解的,如果有高人,可以來解釋下為什么這樣。
if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;下面的代碼也很明白了,如果不是上面都 ,那么如果是樹節(jié)點,那么就執(zhí)行下面的邏輯,此處不再深究。
else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);下面的代碼的含義是,如果p.next為空的話,那么新建一個節(jié)點,并追加到尾部,這種情況,就是當(dāng)p指向最后一個節(jié)點的時候才會出現(xiàn)的情況。
for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}后面的代碼有點感覺太難啃了,先暫時就這樣吧,今天的HashMap分析得還不太完整,并且不太合理,希望大家能多提寶貴建議。
?
轉(zhuǎn)載于:https://www.cnblogs.com/kmsfan/p/8027184.html
總結(jié)
以上是生活随笔為你收集整理的HashMap原理总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TPL中Task执行的内联性线程重入
- 下一篇: setTimeout 定时器的使用