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
由於計算key的下標的時候使用的是:key的hashcode值 &(按位與) 數組的長度-1 ,
固定爲2的次冪能保證碰撞概率小。github
key的hashcode值 & 數組的長度-1 ;
& (與運算)面試
bucket裏的第一個節點,直接命中;
若是有衝突,則經過key.equals(k)去查找對應的entry
若爲樹,則在樹中經過key.equals(k)查找,O(logn);
若爲鏈表,則在鏈表中經過key.equals(k)查找,O(n)。數組
哈希表在新增元素的時候首先會根據hash函數算出這個元素存放的數組的位置,
可是,有可能存在不一樣的key值獲得相同的數組位置(兩個元素的HashCode值相同),這個時候就是哈希衝突。安全
動態數組+鏈表(單向)的方式。
若是某個位置上的鏈表很長,會影響檢索,JDK1.8引入了當鏈表的長度大於8的時候會將鏈表動態替換爲一個紅黑樹,增長了搜索性能。
等於說由:數組+鏈表
變成了數組+鏈表+紅黑樹
數據結構
當HashMapde的長度超出了負載因子與當前容量的乘積(默認16*0.75=12)時,
經過調用resize方法從新建立一個原來HashMap大小的2倍的newTable數組,
並將原先table的元素所有移到newTable裏面,從新計算hash,而後再從新根據hash分配位置,,最大擴容爲2的30次方+1。
負載因子默認是:0.75
多線程
多線程環境下,有可能多個線程同時進行resize,在這過程當中,可能產生死鎖或者死循環。
具體緣由不清楚。併發
HashMap是線程不安全的,因此在多線程的環境中咱們須要尋找替代方案:
Fail-fast 機制是 java 集合(Collection)中的一種錯誤機制,
java.util.HashMap 不是線程安全的,若是在使用迭代器的過程當中有其餘線程修改了 map,
那麼將拋出 ConcurrentModificationException,這就是所謂 fail-fast 策略
這一策略在源碼中的實現是經過 modCount 也就是修改次數實現的。
與之關聯的面試題:
遇到過ConcurrentModficationException(併發修改異常)異常嗎?爲何會出現?如何解決?