在java的容器集合中,hashmap的使用頻率可以說是相當高的。不過對於hashmap的存(put())以及取(get())的原理可能很多人還不大清楚,今天,我就給大家介紹下它是如何存如何取的。

#下面以回答問題的形式來講解#
**假如有面試官問你,hashmap是如何存數據的,你會怎么回答? **
-
我想每個人都知道hashmap是以鍵值對的方式來存數據的,有些人可能會這么回答:當我們執行put(key, value)函數的時候,以key作為鍵,value作為值來存,並且如果key相同的話,則新的value會覆蓋掉舊的value。
-
而有些人可能會這么回答:hashmap在存數據的時候是基於hashing的原理,當我們調用put(key,value)方法的時候,其實我們會先對鍵key調用key.hashcode()方法,根據方法返回的hashcode來找到bucket的位置來存Entry對象。(Entry對象存有key和value)。如下圖:(這里沒有考慮碰撞)

顯然前者和后者的回答,后者的回答還是相對好點的。不過,這可能僅僅只是故事的開始。
**這時面試官可能會問你,如果兩個key對象的hashcode相同怎么辦? **
-
對於不熟悉hashcode()和equals()這兩個方法的人來說,他可能會直接說,因為hashcode相同,那么兩個對象是同一個對象,進而新的value覆蓋掉舊的value。如果你這樣回答,后果你懂 。(當然可能面試會提醒你或直接問你別的問題了)。
-
有些人則會回答,由於hashcode相同,那么它們對應的bucket顯然也是相同的,這個時候就會產生所謂的碰撞(hashmap的底層存儲結構是 數組+鏈表)。每個bucket索引對應一個鏈表,這個時候系統就會找到對應的鏈表,並在鏈表的尾部加上這個Entry對象,如下圖:(圖畫的有點丑,哈哈)

- 這個時候跑出來個第三者,自豪着補充了一句:根據hashcode找到對應的bucket之后,還會在對應的鏈表逐一檢查這個鏈表里有沒存在相同的key對象,這個時候是通過equals這個方法來對比的。如果有,者用新的value取代舊的value。如果沒有,則向樓上說的,在鏈表的尾部加上這個新的Entry對象。
- 這個時候,hashmap的put原理講解就告一段落了。下面說說獲取get(key)原理
- 其實get原理和put原理是差不多的,一個逆向的過程。
- 當我們調用get(key)的時候,會調用key的hashcode方法獲得hashcode.
- 根據hashcode獲取相應的bucket。
- 由於一個bucket對應的鏈表中可能存有多個Entry,這個時候會調用key的equals方法來找到對應的Entry
- 最后把值返回(這句好像是廢話....但我還是想說下)。
繼續漲知識......
和其他容器一樣,當我們沒有指定大小直接new一個hashmap的時候

系統會自動給我們初始化一個數值。如果我們在存數據的過程中,大小超過了負載因子定義的容量怎么辦?
-
這里先給大家解釋下 負載因子:負載因子(load factor,假設大小為n)就是當一個map填滿了n倍的bucket的時候,hashmap就會進行擴容。
-
其實當一個map被填滿到75%的時候(默認的負載因子大小是0.75),它就會進行擴容,創建一個大小是原理兩倍的bucket數組,並且將原理的數據存放到新的數組里。
大家都知道,當Map在擴容新的數組並且移動數據的時候,都是比較消耗時間和內存的,如果我們事先能預測到我們到存的數據的大致大小的話,我們就可以新創建hashmap的時候指定大小,這樣,可以大小減少擴容帶來的消耗。
- 這里可能大家有一些疑問,例如為啥默認的負載因子大小是0.75呢(看有些人在討論這個問題)。對於這個我覺得可能是通過大量的數據測出來的(還沒有去百度看別人的解答,僅代表個人觀點,歡迎你們的解答)
- 這里在給大家解釋以下負載因子的作用(可能有些人還不知道負載因子的干啥用的)
- 負載因子越大,數組要被填滿時,元素就會越多,元素越多,沖突的幾率就會越大,一個鏈表存的元素也會越多,查詢的時候就會越慢。但是,此時空間的利用率更高了----空間換時間
- 負載因此越小,數組要被填滿時,元素就會越少,沖突也會也少,一個鏈表的元素也會越少,查詢的時候也就越快。但是,空間的利用率低了-----時間換空間。
如果你習慣在微信公眾號看技術文章
想要獲取更多資源的同學
歡迎關注我的公眾號:苦逼的碼農
每天向你推送 各種視頻資源,電子書、 技術文章以及
和面試有關的 算法專題 每日一題