一個HashMap能跟面試官扯上半個小時
阿新 • • 發佈:2020-07-12
### 一個HashMap能跟面試官扯上半個小時
> 《安琪拉與面試官二三事》系列文章
> [一個HashMap能跟面試官扯上半個小時](https://blog.csdn.net/zhengwangzw/article/details/104889549)
> [一個synchronized跟面試官扯了半個小時](https://blog.csdn.net/zhengwangzw/article/details/105141484)
>[一個volatile跟面試官扯了半個小時](https://angela.blog.csdn.net/article/details/106060526)
> 《安琪拉教魯班學演算法》系列文章
>
> [安琪拉教魯班放技能之動態規劃](https://blog.csdn.net/zhengwangzw/article/details/105321718)
## 前言
HashMap應該算是Java後端工程師面試的必問題,因為其中的知識點太多,很適合用來考察面試者的Java基礎。
## 開場
**面試官**: 你先自我介紹一下吧!
**安琪拉**: 我是安琪拉,草叢三婊之一,最強中單(鍾馗不服)!哦,不對,串場了,我是**,目前在--公司做--系統開發。
**面試官**: 看你簡歷上寫熟悉Java集合,HashMap用過的吧?
**安琪拉**: 用過的。(還是熟悉的味道)
**面試官**: 那你跟我講講HashMap的內部資料結構?
**安琪拉**: 目前我用的是JDK1.8版本的,內部使用陣列 + 連結串列紅黑樹;
**安琪拉**: 方便我給您畫個數據結構圖吧:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWdrci5jbi1iai51ZmlsZW9zLmNvbS85ZDkyZGRkYS1lZmRiLTRmZjctYTlmYi00MTFjMTY5MzNkYmMucG5n?x-oss-process=image/format,png)
**面試官**: 那你清楚HashMap的資料插入原理嗎?
**安琪拉**: 呃[做沉思狀]。我覺得還是應該畫個圖比較清楚,如下:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/2020031712385760.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poZW5nd2FuZ3p3,size_16,color_FFFFFF,t_70)
1. 判斷陣列是否為空,為空進行初始化;
2. 不為空,計算 k 的 hash 值,通過`(n - 1) & hash`計算應當存放在陣列中的下標 index;
3. 檢視 table[index] 是否存在資料,沒有資料就構造一個Node節點存放在 table[index] 中;
4. 存在資料,說明發生了hash衝突(存在二個節點key的hash值一樣), 繼續判斷key是否相等,相等,用新的value替換原資料(onlyIfAbsent為false);
5. 如果不相等,判斷當前節點型別是不是樹型節點,如果是樹型節點,創造樹型節點插入紅黑樹中;(如果當前節點是樹型節點證明當前已經是紅黑樹了)
6. 如果不是樹型節點,建立普通Node加入連結串列中;判斷連結串列長度是否大於 8並且陣列長度大於64, 大於的話連結串列轉換為紅黑樹;
7. 插入完成之後判斷當前節點數是否大於閾值,如果大於開始擴容為原陣列的二倍。
**面試官**: 陷入沉默,講的這麼清楚,難道是也關注了微信公眾號【安琪拉的部落格】,我繼續按照套路問,剛才你提到HashMap的初始化,那HashMap怎麼設定初始容量大小的嗎?
**安琪拉**: [這也算問題??] 一般如果`new HashMap()` 不傳值,預設大小是16,負載因子是0.75, 如果自己傳入初始大小k,初始化大小為 大於k的 2的整數次方,例如如果傳10,大小為16。(補充說明:實現程式碼如下)
```java
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
```
> 補充說明:下圖是詳細過程,演算法就是讓初始二進位制右移1,2,4,8,16位,分別與自己位或,把高位第一個為1的數通過不斷右移,把高位為1的後面全變為1,最後再進行+1操作,111111 + 1 = 1000000 = $2^6$ (符合大於50並且是2的整數次冪 )
>
>![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200317235036486.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poZW5nd2FuZ3p3,size_16,color_FFFFFF,t_70)
**面試官**: 你提到hash函式,你知道HashMap的雜湊函式怎麼設計的嗎?
**安琪拉**: [問的還挺細] hash函式是先拿到 key 的hashcode,是一個32位的int值,然後讓hashcode的高16位和低16位進行異或操作。
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWdrci5jbi1iai51ZmlsZW9zLmNvbS81ZmRlYTk1My0xMDg5LTQ3NjctOGYzMC1iYWYwZTZlMTk5ZmIucG5n?x-oss-process=image/format,png)
**面試官**: 那你知道為什麼這麼設計嗎?
**安琪拉**: [這也要問],這個也叫擾動函式,這麼設計有二點原因:
1. 一定要儘可能降低hash碰撞,越分散越好;
2. 演算法一定要儘可能高效,因為這是高頻操作, 因此採用位運算;
**面試官**: 為什麼採用hashcode的高16位和低16位異或能降低hash碰撞?hash函式能不能直接用key的hashcode?
[這問題有點刁鑽], 安琪拉差點原地