HashMap資料結構--java基礎面試大Boss
前言:
幾年前剛剛培訓java出來,參加的第一家面試,面試java基礎,第一個問題就是hashmap資料結構。
當時我的反應是這樣的:
啥?啥結構,什麼數?什麼構?不就是get、put?
後來經過百度,看各種帖子,也算是瞭解了一些,面試時也算是能跟面試官說說hashmap資料結構了,畢竟天天問。問道最後也能“談笑風聲”了。。。。
後來在工作之餘慢慢看了資料結構這本數,看了下資料結構的視訊(這裡強推趙海英的視訊),發現當時的我還真是圖樣圖森破、sometime naive。
正文:
網上許多帖子也都提到:hashMap資料結構是陣列+連結串列,但是並沒有說為什麼要做成陣列加連結串列,這樣做有什麼好處,hash查詢到底是什麼這些都沒又提到,下面說說hash又名雜湊,在hashMap中的使用。
一: Map,在hashMap中存的最小單元便是Entry,裡面存放key和value。
二:重點在於java中hashMap中的hash是怎麼儲存這個資料的
1.hash函式
這些資料會存在一個數組中,hashMap的陣列結構:
這個陣列中,hash是如何實現這個快速查詢的,比如下面的這一個陣列中存入了許多
我們要通過key去取value的時候,需要便利這一整個陣列,將每一個數據拿出來,去比較key,相同的話便取出來。這樣一來取出資料的時間複雜度便是O(n),也就是說一個長度為n的陣列,取出資料的最多比較次數是n次。那麼有沒有一種方法只要比較一次就能取出value的呢?hash(雜湊)就是實現這個事情的。
java中程式碼是這樣的:
用過key,進行hash函式計算,直接取到節點。
根據key的hashCode計算出一個hash值(>>>移位符,右移一位最大值添0,正數時相當於除2,^異或運算)
注:為了方便理解,下面方法沒截全
根據計算出來的hash值和陣列長度,直接計算出存放資料的索引。這樣hash便實現了只需一次就可以取出value的方法。
以上就是java通過hash函式直接確定資料索引的過程。
注:&與運算,在這個地方(length-1)&hash,其中length在hashmap中是2的N次方,所以這個計算所算出來的值相當於hash%length。(2的N次方的二進位制一定是1111...11這種數字,所以在使用&運算的時候相當於取餘)所以java中hashMap的hash函式使用的是除留取餘法。
2.hash衝突
通過hash函式可以之間計算出索引了,但是在put的時候,插入物件的key計算出的hash值的索引在陣列中已經存在了資料怎麼辦,在hash查詢中不可避免出現hash衝突。java中便是使用連結串列來處理hash衝突的(注:在java8中連結串列過長時會變成樹)
原始碼:
以上便是為何hashMap是陣列加連結串列結構。並且如何實現快速hash查詢。
3.hash擴容
當hashMap中資料不斷增多,hash衝突也逐漸增多。陣列中的連結串列也會越來越長,這時hash函式所帶來的快速查詢優勢便逐漸消失,因為在連結串列中查詢便需要重頭到尾遍歷。
所以在資料到達一定數量時便會執行resize方法,重新組成hashMap
這個方法中便會重新排布hashMap。
這裡有一個重要的變數裝載因子:
如果它的值大的話,hashMap中的元素的hash衝突會增多連結串列會越拉越長,get效率低;值小的話hash衝突降低,但是執行resize的次數也會更加頻繁,put的效率會降低。java中預設的是0.75。
所以p3c外掛也會提示
在hashMap初始化時指定大小,減少resize的情況。
以上便是java中對hashMap中的一些操作。
所以在談起hash結構時,主要可以基於三點:hash函式、hash衝突解決、裝載因子。