面試必備系列不會長篇理論求證,直接上答案,僅供參考,不喜勿噴。
1、能說說HashMap的底層原理嗎?
HashMap<String,String> map = new HashMap<String,String>();
map.put(“key”,”value”);
[<key1,value1>,<key2,value2>,<key3,value3>]
HashMap底層實現是數組+鏈表,用來存儲<key,value>形式的數據,當我們調用put(key,value)時,首先會通過hash(key) 來獲取key的hash值,hash值對數組長度進行取模運算,定位到數組的一個存儲位置 bucket,如果bucket沒有發生沖突的話則直接放入數組,發生沖突的話則以鏈表的形式存儲,jdk1.8之后引入了紅黑樹,鏈表的長度超過8之后會使用紅黑樹,小於6之后則又轉換回來。
如下2、3條皆為第1條的補充,預防面試官細問。
2、jdk1.8中對hash算法和尋址算法的優化
jdk1.8中對hash算法進行了優化,之前在對key進行hash(key)計算時,采用的是取模運算,即第1條提到的,而優化后采用的是尋址算法,即:(n-1) & hash 『n為數組長度』
為什么要使用尋址算法呢?首先 「hash & (n-1)」 效果跟 hash 對 n 取模的效果是一樣的, 但是『&』與運算的性能要優於 hash 對 n 取模。
3、hash沖突?怎么解決?
當我們put<key,value>時,首先通過hash(key)計算得到的hash值,再通過『&』與運算之后,得到了數組存儲位置bucket,但此時有可能出現兩個不同的key卻計算出相等的bucket,舉個例子:
數組A[0]位置計算出存放<張三,我是張三>數據,而在put<李四,我是李四>數據時,也計算為存放在A[0]位置,一個位置想存放兩個數據?這就出現hash沖突了,怎么處理呢?
JDK是這樣處理的,它會在這個位置(A[0])掛一個鏈表,這個鏈表表里面存放出現沖突的數據,即:讓多個<key,value>數據同時放在數組的一個位置里。
get(key)時怎么取呢?當我們調用get(key)定位到數組位置時,如果發現這個位置掛載的是一個鏈表,那么就遍歷鏈表,從里面找到自己想要的那個<key,value>數據。
格外補充:這個地方,在JDK1.8之后引入了紅黑樹的概念,首先我們看一下為啥要引入紅黑樹,如果沒有引入紅黑樹,當數組掛載的鏈表達到一定長度之后,查詢是非常耗時的,性能比較差,時間復雜度為:O(n)「讀作:偶en」。
JDK1.8的優化就是,當鏈表的長度發到了一定長度后(8)會自動轉換為紅黑樹,遍歷一棵紅黑樹查找一個元素的時間復雜度為:O(logn)「讀作:偶,老個en」,性能相對鏈表要高一些。
簡單總結一下:
-
出現hash沖突的原因?兩個不同的key計算出相同的數組存放位置;
-
初期是怎么解決的?在出現數組沖突的位置掛一個鏈表,實現存放多個數據。
-
JDK1.8的優化?當數組長度達到一定值后自動轉換為紅黑樹,降低時間復雜度。
4、HashMap是如何擴容的?
HashMap底層是一個數組,當數組滿了之后,他會自動進行2倍擴容,用於盛放更多的數據。
比如,本來數組默認長度=16,擴容后*2=32。
擴容后還有一步操作:rehash,重新對每個hash值進行尋址,也就是用每個hash值跟新的數組長度 n-1 進行『&』與運算操作。
補充:擴容之后的與運算可能會導致之前的發生hash沖突的元素不再發生沖突。
HashTable使用synchronized關鍵字來保證線程安全。當一個線程訪問HashTable的同步方法,其他線程也訪問HashTable的同步方法就會進入阻塞或輪訓狀態。這個的同步方法包括讀和寫,可以理解HashTable只有一把鎖,所有的線程不管做什么,都是競爭這一把鎖,例如線程1使用put進行元素添加,線程2不但不能使用put來添加元素,也不能使用get方法來獲取元素,顯然這效率是多低。