HashMap常見面試題整理

花了三天時間來仔細閱讀hashMap的源碼,期間補了下很多數據結構的知識,刷了很多相關的面試題並進行了整理 git

1.談一下HashMap的特性?github

1.HashMap存儲鍵值對實現快速存取,容許爲null。key值不可重複,若key值重複則覆蓋。 面試

2.非同步,線程不安全。 算法

3.底層是hash表,不保證有序(好比插入的順序)api

 

2.談一下HashMap的底層原理是什麼?數組

基於hashing的原理,jdk8後採用數組+鏈表+紅黑樹的數據結構。咱們經過put和get存儲和獲取對象。當咱們給put()方法傳遞鍵和值時,先對鍵作一個hashCode()的計算來獲得它在bucket數組中的位置來存儲Entry對象。當獲取對象時,經過get獲取到bucket的位置,再經過鍵對象的equals()方法找到正確的鍵值對,而後在返回值對象。安全

 

3.談一下hashMap中put是如何實現的?數據結構

1.計算關於key的hashcode值(與Key.hashCode的高16位作異或運算) 函數

2.若是散列表爲空時,調用resize()初始化散列表 post

3.若是沒有發生碰撞,直接添加元素到散列表中去

4.若是發生了碰撞(hashCode值相同),進行三種判斷

    4.1:若key地址相同或者equals後內容相同,則替換舊值

    4.2:若是是紅黑樹結構,就調用樹的插入方法

    4.3:鏈表結構,循環遍歷直到鏈表中某個節點爲空,尾插法進行插入,插入以後判斷鏈表個數是否到達變成紅黑樹的闕值8;也能夠遍歷到有節點與插入元素的哈希值和內容相同,進行覆蓋。

5.若是桶滿了大於閥值,則resize進行擴容

 

4.談一下hashMap中何時須要進行擴容,擴容resize()又是如何實現的?

調用場景:

1.初始化數組table

2.當數組table的size達到闕值時即++size > load factor * capacity 時,也是在putVal函數中

實現過程:(細講)

1.經過判斷舊數組的容量是否大於0來判斷數組是否初始化過

否:進行初始化

  • 判斷是否調用無參構造器,
    • 是:使用默認的大小和闕值
    • 否:使用構造函數中初始化的容量,固然這個容量是通過tableSizefor計算後的2的次冪數

是,進行擴容,擴容成兩倍(小於最大值的狀況下),以後在進行將元素從新進行與運算複製到新的散列表中

 

 

歸納的講:擴容須要從新分配一個新數組,新數組是老數組的2倍長,而後遍歷整個老結構,把全部的元素挨個從新hash分配到新結構中去。

PS:可見底層數據結構用到了數組,到最後會由於容量問題都須要進行擴容操做

5.談一下hashMap中get是如何實現的?

對key的hashCode進行hashing,與運算計算下標獲取bucket位置,若是在桶的首位上就能夠找到就直接返回,不然在樹中找或者鏈表中遍歷找,若是有hash衝突,則利用equals方法去遍歷鏈表查找節點。

 

6.談一下HashMap中hash函數是怎麼實現的?還有哪些hash函數的實現方式?

對key的hashCode作hash操做,與高16位作異或運算

還有平方取中法,除留餘數法,僞隨機數法

 

7.爲何不直接將key做爲哈希值而是與高16位作異或運算?

由於數組位置的肯定用的是與運算,僅僅最後四位有效,設計者將key的哈希值與高16爲作異或運算使得在作&運算肯定數組的插入位置時,此時的低位實際是高位與低位的結合,增長了隨機性,減小了哈希碰撞的次數。

 

HashMap默認初始化長度爲16,而且每次自動擴展或者是手動初始化容量時,必須是2的冪。

 

8.爲何是16?爲何必須是2的冪?若是輸入值不是2的冪好比10會怎麼樣?

https://blog.csdn.net/sidihuo/article/details/78489820

https://blog.csdn.net/eaphyy/article/details/84386313

1.爲了數據的均勻分佈,減小哈希碰撞。由於肯定數組位置是用的位運算,若數據不是2的次冪則會增長哈希碰撞的次數和浪費數組空間。(PS:其實若不考慮效率,求餘也能夠就不用位運算了也不用長度必需爲2的冪次)

2.輸入數據若不是2的冪,HashMap經過一通位移運算和或運算獲得的確定是2的冪次數,而且是離那個數最近的數字

 

9.談一下當兩個對象的hashCode相等時會怎麼樣?

會產生哈希碰撞,若key值相同則替換舊值,否則連接到鏈表後面,鏈表長度超過闕值8就轉爲紅黑樹存儲

 

10.若是兩個鍵的hashcode相同,你如何獲取值對象?

HashCode相同,經過equals比較內容獲取值對象

11."若是HashMap的大小超過了負載因子(load factor)定義的容量,怎麼辦?

超過闕值會進行擴容操做,歸納的講就是擴容後的數組大小是原數組的2倍,將原來的元素從新hashing放入到新的散列表中去。

12.HashMap和HashTable的區別

相同點:都是存儲key-value鍵值對的

不一樣點:

  • HashMap容許Key-value爲null,hashTable不容許;
  • hashMap沒有考慮同步,是線程不安全的。hashTable是線程安全的,給api套上了一層synchronized修飾;
  • HashMap繼承於AbstractMap類,hashTable繼承與Dictionary類。
  • 迭代器(Iterator)。HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。因此當有其它線程改變了HashMap的結構(增長或者移除元素),將會拋出ConcurrentModificationException。
  • 容量的初始值和增長方式都不同:HashMap默認的容量大小是16;增長容量時,每次將容量變爲"原始容量x2"。Hashtable默認的容量大小是11;增長容量時,每次將容量變爲"原始容量x2 + 1";
  • 添加key-value時的hash值算法不一樣:HashMap添加元素時,是使用自定義的哈希算法。Hashtable沒有自定義哈希算法,而直接採用的key的hashCode()。

 

13.請解釋一下HashMap的參數loadFactor,它的做用是什麼?

loadFactor表示HashMap的擁擠程度,影響hash操做到同一個數組位置的機率。默認loadFactor等於0.75,當HashMap裏面容納的元素已經達到HashMap數組長度的75%時,表示HashMap太擠了,須要擴容,在HashMap的構造器中能夠定製loadFactor。

 

14.傳統hashMap的缺點(爲何引入紅黑樹?):

JDK 1.8 之前 HashMap 的實現是 數組+鏈表,即便哈希函數取得再好,也很難達到元素百分百均勻分佈。當 HashMap 中有大量的元素都存放到同一個桶中時,這個桶下有一條長長的鏈表,這個時候 HashMap 就至關於一個單鏈表,假如單鏈表有 n 個元素,遍歷的時間複雜度就是 O(n),徹底失去了它的優點。針對這種狀況,JDK 1.8 中引入了 紅黑樹(查找時間複雜度爲 O(logn))來優化這個問題。

 

15. 平時在使用HashMap時通常使用什麼類型的元素做爲Key?

選擇Integer,String這種不可變的類型,像對String的一切操做都是新建一個String對象,對新的對象進行拼接分割等,這些類已經很規範的覆寫了hashCode()以及equals()方法。做爲不可變類天生是線程安全的,

 

源碼解析閱讀:

1.https://blog.csdn.net/u011240877/article/details/53358305#%E4%BC%A0%E7%BB%9F-hashmap-%E7%9A%84%E7%BC%BA%E7%82%B9

2. http://www.javashuo.com/article/p-csdeqcso-dm.html

3. http://www.javashuo.com/article/p-uqkohdtn-dg.html

4. https://blog.csdn.net/panweiwei1994/article/details/76555359#commentBox

5.

 

更多關於hashMap集合的面試題:

https://zhuanlan.zhihu.com/p/32355676

https://zhuanlan.zhihu.com/p/40760616

http://www.javashuo.com/article/p-qlgzvacj-z.html

https://www.jianshu.com/p/7af5bb1b57e2

https://baiqiantao.github.io/Java/%E9%9B%86%E5%90%88/3AFbAb/

相關文章
相關標籤/搜索