必懂知識——HashMap的實現原理

HashMap的底層數據結構

  • 1.7以前是:數組+鏈表
    數組的元素是Map.Entiry對象
    當出現哈希碰撞的時候,使用鏈表解決,
    先計算出key對應的數組的下標,這個數組的這個位置上爲空,直接放入,
    若是不爲空並且出現哈希碰撞,就把元素添加到鏈表的頭部的,
    new Entry(key,value,table[i]);這樣這個Entry就是鏈表的頭部了,而後放到數組的index位置上。java

  • 1.8是:數組+鏈表+紅黑樹
    數組的元素是Map.Node對象繼承Map.Entry包含屬性有:
    當前node對象的hash值,
    當前node對象的key,
    當前node對象的下一個節點對象next,
    當前node對象的valuenode

  • 1.8爲何使用數組+鏈表+紅黑樹?
    由於若是一個hashmap在同一個數組位置上出現hash碰撞過多,那麼這個鏈表的長度會很長,
    插入塊,可是查詢由於使用的遍歷因此會比較慢。git

HashMap的大體結構


HashMap的關鍵屬性有哪些?

  • 數組的默認大小16
  • 數組的最大爲2的30次方
  • 擴容因子0.75
  • threshold
  • 當前數組的大小

初始大小或者擴容的數組大小爲何必定是2的次冪?

由於計算key的下標的時候使用的是:key的hashcode值 &(按位與) 數組的長度-1 ,
固定爲2的次冪能保證碰撞概率小。github

怎麼經過key計算獲得數組的下標的?

key的hashcode值 & 數組的長度-1 ;
& (與運算)面試

hashMap的大小是怎麼計算的?

  • 有一個全局變量size,每次put的時候++,remove的時候--;

HashMap中Put方法的工做原理

  • 獲取key的hashCode()值,經過位運算獲得數組的下標。
  • 若是該位置上爲空直接放入該數組的下標位置。
  • 若是不爲空出現了碰撞,去遍歷鏈表或者樹,利用key的equal()方法是否存在相同的節點,若是相同就覆蓋,不相同就放到鏈表的尾部。
  • 若是碰撞致使鏈表過長,長度大於等於8,就把鏈表轉換成紅黑樹;
  • 若是數組的容量超過了總容量*負載因子的值,就要resize。

HashMap中Get方法的工做原理

bucket裏的第一個節點,直接命中;
若是有衝突,則經過key.equals(k)去查找對應的entry
若爲樹,則在樹中經過key.equals(k)查找,O(logn);
若爲鏈表,則在鏈表中經過key.equals(k)查找,O(n)。數組

什麼是哈希衝突?

哈希表在新增元素的時候首先會根據hash函數算出這個元素存放的數組的位置,
可是,有可能存在不一樣的key值獲得相同的數組位置(兩個元素的HashCode值相同),這個時候就是哈希衝突。安全

HashMap怎麼處理哈希衝突(哈希碰撞的)

動態數組+鏈表(單向)的方式。

若是某個位置上的鏈表很長,會影響檢索,JDK1.8引入了當鏈表的長度大於8的時候會將鏈表動態替換爲一個紅黑樹,增長了搜索性能。
等於說由:數組+鏈表

變成了數組+鏈表+紅黑樹
數據結構

HashMap中的數組何時擴容(rehash)

當HashMapde的長度超出了負載因子與當前容量的乘積(默認16*0.75=12)時,
經過調用resize方法從新建立一個原來HashMap大小的2倍的newTable數組,
並將原先table的元素所有移到newTable裏面,從新計算hash,而後再從新根據hash分配位置,,最大擴容爲2的30次方+1。
負載因子默認是:0.75
多線程

你瞭解從新調整HashMap大小存在什麼問題嗎

多線程環境下,有可能多個線程同時進行resize,在這過程當中,可能產生死鎖或者死循環。
具體緣由不清楚。併發

多線程下的HashMap

HashMap是線程不安全的,因此在多線程的環境中咱們須要尋找替代方案:

  • 使用Map m = Collections.synchronizedMap(new HashMap(...));實現同步
  • 使用java.util.HashTable,效率最低(由於全部的方法都是同步的,幾乎被淘汰了)
  • 使用java.util.concurrent.ConcurrentHashMap,相對安全,效率高(建議使用)

Fail-Fast 機制

Fail-fast 機制是 java 集合(Collection)中的一種錯誤機制,
java.util.HashMap 不是線程安全的,若是在使用迭代器的過程當中有其餘線程修改了 map,
那麼將拋出 ConcurrentModificationException,這就是所謂 fail-fast 策略
這一策略在源碼中的實現是經過 modCount 也就是修改次數實現的。

與之關聯的面試題:
遇到過ConcurrentModficationException(併發修改異常)異常嗎?爲何會出現?如何解決?

HashMap和Hashtable的區別

  • 主要區別:Hashtable是線程安全,而HashMap則非線程安全。
  • HashMap可使用null做爲key,而Hashtable則不容許null做爲key
  • HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的
    就是當有其它線程改變了HashMap的結構(增長或者移除元素),將會拋出ConcurrentModificationException
  • 因爲Hashtable是線程安全的也是synchronized,因此在單線程環境下它比HashMap要慢

資源

小灰灰的講解

一文搞定HashMap的實現原理和麪試

HashMap工做原理及實現

相關文章
相關標籤/搜索