hashmap扩容 面试_hashmap经典面试问题以及答案

上一篇文章分析了hashmap的主要方法,了解了主要方法接下来好解答平时面试的问题了。

加载因子为什么是 0.75?

那加载因子为什么是 0.75 而不是 0.5 或者 1.0 呢?

首先如果加载因子比较大,那么扩容发生的频率就比较低,但是他浪费的空间比较小,不过发生hash冲突的几率就比较大,比如加载因子是1的时候,如果hashmap长度为128,那么可能hashmap的实际存储元素数量在64至128之间的时间段比较多,而这个时间段发生hash冲突就比较大,造成数组中其中一条链表较长,就会影响性能。

而当加载因子值比较小的时候,扩容的频率就会变高,因此会占用更多的空间,但是元素的存储就比较稀疏,发生哈希冲突的可能性就比较小,因此操作性能会比较高,比如设置成0.5,同样128长度的hashmap,当数量达到65的时候就会触发hashmap的扩容,扩容后长度为256,256里面只存储了65个似乎有点浪费了。

所以综合了以上情况就取了一个 0.5 到 1.0 的平均数 0.75 作为加载因子。

当有哈希冲突时,HashMap 是如何查找并确认元素的?

通过昨天分析get方法应该已经比较清楚了,如果哈希冲突,还是找到table[index]的第一个Node,然后一个一个去比对链表中的key,key一致则找到,引用put流程图其中一部分如下图:

fed279f20e7f44ec5920a6a20f2434b5.png

JDK 1.8 HashMap 扩容时做了哪些优化?

在 JDK 1.7 中 HashMap 是以数组加链表的形式组成的,JDK 1.8 之后新增了红黑树的组成结构,当链表大于 8 并且容量大于 64 时,链表结构会转换成红黑树结构,即使在hashcode 完全相同极端情况下,由于红黑树的特点,查找某个特定元素,也只需要O(log n)的开销,而如果查找链表,时间复杂度会退化到 O(n)。

HashMap 是线程安全的吗,为什么不是线程安全的?

通过例子来讲解这个不安全的过程,引用上一篇文章的例子,首先一个长度为8的map,map已经存了key为1、9的两个值,他们都会在map的table[1]上。

1、首先线程1put一个key=17的值,当程序运行到判断key=9的下一个节点为null准备把key=17的值设置为它的下一个节点时,线程让出资源。

2、此时执行线程2,对mapput一个key=25的数据,这个数据正常作为key=9的next正常插入。

3、线程1再次拿到资源继续执行,设置key=9的next为key=17,这样线程2设置的key=25就被覆盖。

以上的流程示意图如下图:

85c155c91aa6ffd2ed48069b769159b3.png

关键代码在于HashMap的put代码,详解如下图:

a88bbb56e84df4563082e37d08b7df12.png

HashMap 是如何导致死循环的?

说到死循环都是说jdk7的hashmap的死循环,主要在多线程情况下,多个线程对map的同时扩容造成的,jdk7扩容的关键代码如下图:

239eaff31feaff10af1d49ef1686b357.png

可以看到jdk7比上一篇文章分析的jdk8的扩容的关键代码就简单多了,实际上就是遍历所以元素,找到在新的table下的位置,并把新table对应位置的元素设置成当前元素的next,就是所谓的头插法!

那么为什么会造成死循环呢?这里简单做一个造成死循环的流程分析,如下图:

c3bbdf393ae027127708c451efef9751.png

造成死循环的原因主要在于线程1执行单key=9,并取出他的next(key=25)作为下次遍历后,线程1交出了执行权休息去了,而接着线程2吭哧吭哧的执行完扩容,扩容由于采用头插法,此时key=25那个Entry对象的next是key=9。

所以当线程1继续执行,到遍历next(key=25)时他的next变成了key=9,最终造成了两个Entry互相引用,如果后面调用map.get(41)就会刚好查询table[9],然后遍历这个链表,造成死循环!

总结

hashmap面试时主要的问题就是上面的那些了,有些可能没有覆盖到,但是只要弄懂了源码,所有问题都差不多,注意jdk8只是解决了死循环的问题,多线程下数据丢失的问题还是没有解决,所以多线程还是用ConcurrentHashMap。

有时候面试的时候可能面试官只会问"你了解hashmap吗?",这种问题就不要想简单了直接回答一个了解就完了,而是把你理解的比如hashmap结构、主要方法实现、jdk7与jdk8的区别、jdk7可能的问题等都可以主动说出来,这样会更好!

Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!

108fc56402341c031dfb95eee4494931.png
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值