面试官超级喜欢问的HashMap

# 前言
>阿巴阿巴约了面试,面试官问她对HashMap的理解。

**<Center>回去等通知</Center>**

**面试官:** HashMap了解吗?来谈谈你对这块的理解。

**阿巴阿巴:** 嗯嗯好,先从HashMap的结构开始吧,HashMap是一种散列表,由数组+链表+红黑树组成,初始默认的容量是16,负载因子是0.75,当链表上的元素大于8时链表进行红黑树化,当红黑树上元素减少到6时,红黑树会退化为链表。

**阿巴阿巴:** HashMap主要有2个重要方法,put/get,put方法就是将元素无序的加入到HashMap中,也就是无法保证元素的插入顺序,get方法就是获取HashMap中的元素。

**阿巴阿巴:** 此外还有一个扩容的方法,扩容阈值为当前容量 * 负载因子,每次扩容后的容量都是之前容量的2倍。

**面试官:**  很好,还有吗?

**阿巴阿巴:** 额......好像容量必须是2的次幂,嗯....额.....

**面试官:** 好的,今天面试先到这里,你先**回去等通知**😈。
<br>
> 对HashMap理解不深,没有将知识点发散的思维。

这一年阿巴阿巴发奋图强,再次约面。

<br>

**<Center>当场发offer</Center>**

**面试官:** HashMap了解吗?来谈谈你对这块的理解。

**阿巴阿巴:** 嗯嗯好,先从HashMap的结构开始吧,HashMap是一种散列表,以Key/Value形式存储,Key和Value都可以为空,在JDK 1.7时是由数组+链表组成,JDK 1.8则由数组+链表+红黑树组成,这里主要介绍下JDK 1.8版本的HashMap,初始默认的容量是16,负载因子是0.75,当链表上的元素大于8**且数组容量大于64**时链表进行红黑树化,当红黑树上元素减少到6时,红黑树会退化为链表。


**阿巴阿巴:** 数组的查询效率是O(1),链表的查询效率是O(N),红黑树的查询效率是O(logN)。

**阿巴阿巴:** 还有,HashMap的容量必须是2的次幂,在构造方法中传入的容量如果不是2的次幂,那么HashMap会调用tableSizeFor()方法来获取一个最接近传入容量且大于传入容量的2的次幂的值,比如传入的容量是17则tableSizeFor()会返回32。

**面试官:** 不错不错。

**阿巴阿巴:** HashMap空参数的构造方法创建出来的HashMap对象是不会初始化空间的,当使用空参构造方法创建出对象后,HashMap将在第一次插入元素时进行空间的初始化。

```
// 空参构造方法
public HashMap(){
this.loadFactor = DEFAULT_LOAD_FACTOR;
}
```
**阿巴阿巴:** HashMap主要有2个重要方法,put()/get(),put()方法就是将元素无序的加入到HashMap中,也就是无法保证元素的插入顺序,get方法就是获取HashMap中的元素。

**阿巴阿巴:** 当元素进行put时,首先要计算key的Hash值,为了更加分散,获取hash值后,HashMap会让hash值的高16位与hash进行异或。

```
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
```
**阿巴阿巴:** 然后判断HashMap有没有进行初始化,如果没有则进行初始化,再找到Key对应数组的位置,如果该位置没有元素则直接插入,如果有元素则判断是不是key与所在元素的key是不是一致,一致则新值替换旧值,如果不一致继续遍历这个节点下的链表或红黑树。

**阿巴阿巴:** 如果遍历过程中有元素的key与所在元素的key是一致的则新值替换旧值,否则将改元素加入到链表或红黑树中。

**阿巴阿巴:** get()方法首先计算key的hash值,然后找到hash对应数组中的位置的第一个元素,判断key是否一致,如果一致则返回否则继续查找这个元素下的链表或红黑树。

**面试官:** HashMap的扩容你了解吗?

**阿巴阿巴:** HashMap的扩容会调用resize()方法,扩容阈值为当前容量 * 负载因子,如果默认情况下hashMap容量为16,负载因子是0.75,则元素个数大于12就会触发扩容,resize()这个方法会先判断HashMap是不是初始化了,扩容的时候会创建一个新的数组,然后旧数组中的元素进行搬移,JDK1.7 的HashMap在并发场景下扩容有可能会造成死循环,cpu飙升100%。

**面试官:** 嗯,那你知道为啥链表转红黑树的阈值是8吗?

**阿巴阿巴:** 哇,这个啊,这个在HashMap源码里有介绍,简单翻译就是:在使用分布良好的哈希码时,树是很少被使用的。理想情况下,随机哈希码遵循**泊松分布**,从上面给出的数据看,一个链表上有八个节点的概率为0.00000006 这个值要小于千万分之一。当这个阀值为8已经很小了,所以这就是阈值选8的原因。

**面试官:** 你刚有讲在put()/get()方法中要判断元素是否一致,那是如何判断元素一致的呢?

**阿巴阿巴:** 先判断hash值是不是相等,如果相等再进行equals()判断,如果仍相等,则认为这两个元素一致。

**面试官:** 好的,明天来上班。

**阿巴阿巴:** 嗯嗯,再见!
# 总结

> 对常用方法及过程进行了讲解,以及涉及到的一些细节进行了阐述,发散了一些知识点。面试不要慌记住下面六点即可。
- 1、**版本**打好预防针(1.7\1.8版本)
- 2、**数据结构**没法跑(数组 + 链表 + 红黑树)
- 3、**内置属性**少不了(初始容量16、负载因子0.75、树化阈值8、树转链表阈值6、扩容大小2倍)
- 4、**put/get**要记牢(把基本的put、get流程记牢)
- 5、**扩容方法**不忘掉(扩容方法也要知道流程、并发环境下1.7版本扩容会造成死锁)
- 6、边边角角都够到(对比了hash为啥还要对比equals方法,树化阈值为啥是8,key和value能为空吗,扩容一定是达到阈值才能扩容吗) 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值