1.jdk1.8中hashMap的原理,hash衝突如何解決

一:hashMap的工作原理

       HashMap是基於鏈地址法的原理,使用put(key, value)存儲對象到HashMap中,使用get(key)從HashMap中獲取對象。

       當我們給put()方法傳遞鍵和值時,我們先對鍵調用hashCode()方法計算hash從而得到bucket位置,進一步存儲,HashMap會根據當前bucket的佔用情況自動調整容量(超過Load Facotr則resize爲原來的2倍),儲存Node對象。

       使用get(key)從HashMap中獲取對象時,它調用hashCode計算hash從而得到bucket位置,並進一步調用equals()方法確定鍵值對。如果發生碰撞的時候,Hashmap通過鏈表將產生碰撞衝突的元素組織起來,在Java 8中,如果一個bucket中碰撞衝突的元素超過某個限制(默認是8),則使用紅黑樹來替換鏈表,從而提高速度。

二:hash衝突

       如果兩個不同對象的hashCode相同,這種現象稱爲hash衝突。

三:hash衝突的解決辦法

     在hashMap中,採用鏈地址法解決hash衝突。

常用方法有:開發定址法、再哈希法、鏈地址法、建立公共溢出區。

(1)開放定址法

       這種方法也稱再散列法,其基本思想是:當關鍵字key的哈希地址p=H(key)出現衝突時,以p爲基礎,產生另一個哈希地址p1,如果p1仍然衝突,再以p爲基礎,產生另一個哈希地址p2,…,直到找出一個不衝突的哈希地址pi ,將相應元素存入其中。這種方法有一個通用的再散列函數形式:

Hi=(H(key)+di)% m   i=1,2,…,n

其中H(key)爲哈希函數,m 爲表長,di稱爲增量序列。增量序列的取值方式不同,相應的再散列方式也不同。主要有以下三種:

      1)線性探測再散列

          dii=1,2,3,…,m-1

          這種方法的特點是:衝突發生時,順序查看錶中下一單元,直到找出一個空單元或查遍全表。

     2)二次探測再散列

         di=12,-12,22,-22,…,k2,-k2    ( k<=m/2 )

         這種方法的特點是:衝突發生時,在表的左右進行跳躍式探測,比較靈活。

    3)僞隨機探測再散列

         di=僞隨機數序列。

         具體實現時,應建立一個僞隨機數發生器,(如i=(i+p) % m),並給定一個隨機數做起點。

        例如,已知哈希表長度m=11,哈希函數爲:H(key)= key  %  11,則H(47)=3,H(26)=4,H(60)=5,假設下一個關鍵字爲69,則H(69)=3,與47衝突。

        如果用線性探測再散列處理衝突,下一個哈希地址爲H1=(3 + 1)% 11 = 4,仍然衝突,再找下一個哈希地址爲H2=(3 + 2)% 11 = 5,還是衝突,繼續找下一個哈希地址爲H3=(3 + 3)% 11 = 6,此時不再衝突,將69填入5號單元。

        如果用二次探測再散列處理衝突,下一個哈希地址爲H1=(3 + 12)% 11 = 4,仍然衝突,再找下一個哈希地址爲H2=(3 - 12)% 11 = 2,此時不再衝突,將69填入2號單元。

       如果用僞隨機探測再散列處理衝突,且僞隨機數序列爲:2,5,9,……..,則下一個哈希地址爲H1=(3 + 2)% 11 = 5,仍然衝突,再找下一個哈希地址爲H2=(3 + 5)% 11 = 8,此時不再衝突,將69填入8號單元。

(2)再哈希法

       這種方法是同時構造多個不同的哈希函數:

       Hi=RH1(key)  i=1,2,…,k

      當哈希地址Hi=RH1(key)發生衝突時,再計算Hi=RH2(key)……,直到衝突不再產生。這種方法不易產生聚集,但增加了計算時間。

(3)鏈地址法

       這種方法的基本思想是將所有哈希地址爲i的元素構成一個稱爲同義詞鏈的單鏈表,並將單鏈表的頭指針存在哈希表的第i個單元中,因而查找、插入和刪除主要在同義詞鏈中進行。鏈地址法適用於經常進行插入和刪除的情況。

 

(4)建立公共溢出區

       這種方法的基本思想是:將哈希表分爲基本表和溢出表兩部分,凡是和基本表發生衝突的元素,一律填入溢出表。

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