在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呢(看有些人在討論這個問題)。對於這個我覺得可能是通過大量的資料測出來的(還沒有去百度看別人的解答,僅代表個人觀點,歡迎你們的解答)
- 這裡在給大家解釋以下負載因子的作用(可能有些人還不知道負載因子的幹啥用的)
- 負載因子越大,陣列要被填滿時,元素就會越多,元素越多,衝突的機率就會越大,一個連結串列存的元素也會越多,查詢的時候就會越慢。但是,此時空間的利用率更高了----空間換時間
- 負載因此越小,陣列要被填滿時,元素就會越少,衝突也會也少,一個連結串列的元素也會越少,查詢的時候也就越快。但是,空間的利用率低了-----時間換空間。
-
暫時先講到這裡,大家如果有什麼疑問。歡迎提出
-
如果有哪裡講錯了,非常歡迎指點出來
-
如果你覺得有所有所收穫,那就,,,點個讚唄,也希望關注我的公眾號(di201805)勒,我就儘自己的努力來總結學過的知識以及自己的一些感想、看法。