hashmap8如何解決hashmap7中的環鏈問題

先說說對hashmap8的理解:
當我們new一個hashMap的時候,其實沒有做什麼多餘操作,幾乎所有的操作都是在put方法裏面進行的,在map.put()方法中,底層是調用了this.putVal()方法,此方法是核心方法。裏面做了如下邏輯:
1、判斷hashmap是否需要擴容,如果需要則進行擴容,核心方法是resize()。
這個方法非常重要,也非常的消耗性能,因爲涉及到對其中的元素進行地址再分配。既然要重新分配地址,那自然也免不了對每個hash桶的元素挨個遍歷,重新計算地址,這就是爲什麼底層源碼會十分注重性能,以前有的同學一直對hashmap在計算hash散列地址的方法抱有懷疑,原本計算hash值是一件非常簡單的事情-——hashvalue/length就行了,爲什麼一定要使用位與運算,hashval&(length-1)?這樣不光難以理解,而且要保證length是2的指數次冪,豈不是多此一舉?
這是因爲在計算機中取模運算的性能遠遠低於位與運算,而hashmap這種數據結構又經常需要擴容,那麼數據量一旦大起來,需要逐個遍歷其中元素,計算hash散列地址,那麼這個hash散列地址的算法就會大大影響性能了。
再提一下,resize()這個方法,hashmap7中著名的環鏈問題就是由它引起的,這是面試中常見的問題,大家有興趣可以去研究下。

2、判斷hash散列的桶中是否有值,如果有值,就生成鏈表
我們都知道,hashmap的早期數據結構(1.7以前),主要是數組和鏈表組成的,鏈表就是所謂的hash桶,如果元素在表內的散列地址值發生重疊時,就會生成鏈表

3、判斷hash桶內元素是否大於默認閾值TREEIFY_THRESHOLD - 1,如果是,則生成紅黑樹
在這裏插入圖片描述
如上所說,1.7以前我們hashmap主要數據結構是數組和鏈表,那麼1.8相比於1.7發生最大的改變就是增加了紅黑樹,也就是說,1.8的的hashmap是由數組,鏈表,紅黑樹三種數據結構組成的,當鏈表的值達到一定數量的時候,鏈表就會轉化成紅黑樹,這個默認的數量就是TREEIFY_THRESHOLD - 1,然後可能有的同學會問了,爲什麼要把鏈表轉化爲紅黑樹呢?
這個問題其實一兩句說不清楚,可以總結爲,一切都是爲了查詢效率。大家知道,鏈表是一個查詢較慢的數據結構,查詢複雜度達到了O(n),是一個線性數據結構,而紅黑樹查詢複雜度只有O(logn),紅黑樹的樹高不會膨脹的那麼厲害,效率較高。
然後,可能又有同學要問了,爲啥閾值是TREEIFY_THRESHOLD - 1?這個問題我先不說,賣個關子,留給大家去研究。

4、回到主題,hashmap7和hashmap8之間有啥區別呢?hashmap8是如何解決hashmap7中的環鏈問題的呢。首先,我們清楚環鏈問題是怎麼造成的。
在hashmap1.7,擴容的resize()方法中,需要對鏈表中的元素重新計算hash值,遷移到新的tab中去,然而因爲效率問題,源碼開發者認爲新插入的元素訪問熱度較高,所以採用了頭插法,也就是說鏈表尾部的元素將會被提到前面來,是個反順序,鏈表中的元素順序被倒置了。這樣一來,在多線程中就有形成閉環的可能。
例如:當線程A執行到鏈表元素a時,得出它的next元素是b,剛好時間片切換到線程B,線程B將a,b兩個元素都倒置存入到新的tab中,注意,現在b的next就是a了!正當此時,線程A醒來,在線程A中,a的next是b。。。。
也就是說,對於線程a來說,a的next是b,而b的next是a,完美,環鍊形成!!!
如上,我們從例子中得知了,hashmap7形成環鏈的大概過程,爲了避免環鏈問題的形成,hashmap8是怎麼做的呢?
緊接着,我們需要分析出環鍊形成的根本原因,那就是因爲採用了頭插法。頭插法首尾顛倒,反轉了鏈表中元素的順序,才使得線程間形成環鏈。那麼解決這個問題的方式最好的辦法,自然就是不使用頭插法了,事實上,hashmap8也是怎麼做的。
在hashmap8中,鏈表數據的遷移採用的是尾插法,不會再顛倒鏈表(或者說紅黑樹)元素的順序了,當然,除此之外,爲了最大限度的減少樹高和hash碰撞的概率,hashmap8還引出了高低位指針的概念,如下圖代碼:
在這裏插入圖片描述
一條鏈表要做數據遷移到新的tab下,那麼這個散列地址的算法跟hashmap7已經有了區別,它首先需要經過e.hash & oldCap這行代碼來決定鏈表元素的高低位置,等於是分爲了兩條鏈表,低位元素仍然在老的位置,而高位元素卻到了當前位置+oldCap的位置,一條鏈表被拆成了兩條。這樣做的好處如下:
①省卻了再次計算hash值的開銷,避免每個key都進行rehash
②採用尾插法,避免了環鏈的形成

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章