哈希表(HashMap)

(面試題)談談你理解的HashMap?
HashMap本質上是一個用Key,Value結構做存儲的,內部是使用哈希表這種數據結構。
哈希表是非常適合用來做搜索的數據結構,因爲它可以實現插入/刪除/查找時間複雜度是O(1)。
哈希表最重要內部是由數組(利用數組的隨機訪問是O(1)的特性)實現+單鏈表或者其他數據結構(這些數據結構主要是爲了解決Hash衝突)

例:10萬個數可以均勻分佈於1萬個數組元素中,相當於在10個數中查找想要訪問元素。時間複雜度可以看爲O(1)。

HashMap是可以以引用類型來進行存儲,如果需要通過引用類型來進行存儲,我們需要對Key複寫HashCode的方法。得到Hash值怎麼保證Hash值轉化爲數組下標是不越界呢?
java中使用的是:
第一:數組的長度一定是冪次方比如:1,2,4,8,16…
第二:利用Hash值%(數組長度-1)
結合起來可以保證我們的下標是不會越界的。

最後:找到元素爲值Index:再在Hash值對應的鏈表中查找即可。因爲很高效所以一般認爲時間複雜度(O(1))

爲什麼1.7時候使用的是頭插法,1.8變成尾插??

因爲HashMap本來是線程不安全的,但是總是有人將HashMap在多線程環境下使用,如果在多線程環境下使用頭插很可能形成循環鏈表,在查詢就死循環了,CPU100%,這臺機器就什麼都不能幹了。如果改成尾插的話雖然是錯的但是不會出現卡死的現象。也只能算是降低損失。再次提醒多線程下不要使用HashMap。使用ConcurrentHashMap,雖然HashTable是線程安全的,但效率很低(只有一把鎖,一個線程跑其他就不能跑了)

爲什麼HashMap不是線程安全的?

考慮兩方面:1.有沒有寫2.有沒有寫同一個對象(共享資源)

看成鏈表一樣,會有多個線程對數據進行修改,在插入的過程包括擴容中是使數據出現錯誤。

爲什麼在HashMap中的鏈表會退化成紅黑樹

1.hash算法是公開的
如果有人惡意攻擊:肯定可以構建一組數據,這組數據的hash值是一樣的(衝突了),是無法通過調節負載因子來控制衝突率了

解決方法:
1.運算hash的過程中加入其它不公開的信息:私鑰參與hash運算
2.某個下標衝突率過高代表小集合數據也太多了,不適合用鏈表再用其他的數據結構解決。
搜索樹vs哈希表
java選擇紅黑樹解決。
什麼情況下認爲過多了,閾值8個,key的個數足夠多的情況。
根據泊松分佈計算,如果一個index的衝突個數=8的概率是0.00000006
如果真的是8,1.認爲要不是惡意情況 2,認爲數據本身不是均勻分佈的。

java中所有集合基本都是延遲初始化的
Node[ ] table=null
用到的時候再初始化(put數的時候)

HashMap是允許key爲null,其他都不允許Hashtable/ConcurrentHashMap

就插入的過程講解:

1.哈希函數(key)=> 哈希值

  1. 並不要求Key是整形數字
  2. HashMap的key是引用類型,所以,實際上調用的是HashCode()
    如果自定義類作爲key,必須覆寫hashCode()方法
    保證認爲對象相等,則hashCode()得到的 哈希值一定是一樣的

2.如何從哈希值的到一個有效的下標(不越界的下標)

  1. int index = hash%array.length(建立在array.length是素數的基礎上效果最好)

  2. java 前提array.length一定是2的次方,16/32/64/128
    前提:下標一定是32 位(很多時候沒有這麼大)
    hashCode()是32位的
    <1>高16位 -異或上-低16位====>儘可能的使每一位都參與到下標的獲取,並且儘可能的符合均勻分佈。
    <2>h:上一步的結果 . L:數組的長度(一定是2的次方)
    . . .int index = h & (L-1)
    . . . 位用算:
    在這裏插入圖片描述

  3. 根據下標找到數組位置進行插入,引出問題
    衝突:
    1.什麼是衝突
    不同的key,通過運算得到的hash值是相同的
    2.衝突可能消除嗎?
    數組的長度 << 數據的個數,所有數據放在有限個數組元素中
    根據鴿籠原理,必然是存在一個籠子裏有多個鴿子
    3.衝突不能消除的情況,並且不想要衝突,怎麼儘可能避免衝突?
    <1>hash的函數設計:理想情況下,key是符合均勻分佈的
    經過hash函數,得到的hash值也儘可能的均勻分佈。
    <2>負載因子=key的個數/數組的長度
    衝突率的函數曲線
    在這裏插入圖片描述
    4.真衝突了怎麼解決?
    閉散列:此處不留爺,自有留爺處
    開散列/哈希桶: 利用一個其他的數據結構存儲所有衝突的key,之所以選擇鏈表是:一般情況下衝突的個數不會太多,鏈表就夠用了。
    單鏈表每有一個數據就保留一個數據,順序表是需要預留空間。
    HashMap是採用這種方式。

我們的原則是:儘量不惹事但是遇事也不怕事。

.
4 . 一般情況下,在數組當前位置的鏈表中進行插入即可。

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