HashMap面试常见问题分析

首先提出问题

1.HashMap默认容量
2.HashMap如何扩容
3.HashMap的数组大小为什么一定要是2的幂?
4.HashMap为什么是线程不安全的?
5.HashMap在jdk1.7–>1.8做了哪些改进为什么

1.HashMap默认容量默认大小

默认大小为16,注意这里用的是位运算左移四位,位运算效率较高,移动一位就是乘二。
注解的意思是默认大小必须是二的幂次方,一方面是位运算效率较高,还有一个主要原因下面细说。

2.HashMap如何扩容

在这里插入图片描述
上面说到HashMap的初始化容量是8,这里可以看到最大是1<<30,也就是二的三十次方,为什么是这值呢,简单来说因为Integer的最大值是231-1,达不到231
在这里插入图片描述
0.75是默认的负载系数(这个系数是可以改的,但是综合考虑时间和空间平衡官方选择了0.75),就是说在扩容后不超过230的情况下,如果HashMap已使用容量达到75%就会触发扩容机制对容量就行左移一位的操作,也就是翻倍。

3.HashMap的数组大小为什么一定要是2的幂?

除了上面提到的位运算效率快,还有一个原因(其实也还是因为位运算效率快)。
我们知道hashMap的put操作是计算key的hash值然后存到哈希桶(bin)里,初始容量16的数量是bin的大小,也就是说是在极端情况下如果hash值都相等,导致所有数据都存在同一个bin下的时候就会使hashMap数组+链表的数据结构退化为链表,效率大大降低,也就是哈希碰撞。预防这种情况出现做了两个处理:一个是当同一个bin下数量超过8的时候,将链表变为红黑树,二是在存的时候尽量避免出现哈希碰撞,Oracle采取的是将hash值和容量-1进行与运算,即hashCode&(length-1),至于为什么一定要是2的幂的原因也在这: 比如说容量为16,二进制就是10000,length-1就是1111,和要存数据key的哈希值进行与运算的时候因为是1111,所以最后计算出来的结果可能是[0,16)中的任意值,在计算哈希值够科学的情况下,可以做到最终值比较平均,hashmap的最终值是满足泊松分布的。如果大小不是2的幂,就无法保证做与运算的数全为1,如果出现0的话与运算的值一定是0就会出现某些bin始终取不到。至于为什么一定非要与运算呢 当然是因为快啊在这里插入图片描述

4.HashMap为什么是线程不安全的?

1.7版本的JDK,在多线程情况下发生扩容的时候在transfer函数中会因为采取头插法出现闭合环路死锁,不过这里主要讲1.8的。
1.8针对1.7的问题改用了尾插法,但是仍然会出现数据覆盖的情况(至于为什么这坑有空填)。

5.HashMap在jdk1.7–>1.8做了哪些改进为什么

1.7的存储方式是数组+链表变为1.8的数组+链表且在链表大于8时变成红黑树,至于为什么是8,因为hash值的分布满足泊松分布,同一个bin下数据大于8的情况微乎其微,毕竟链表改红黑树也是比较消耗资源的。但是在转变成红黑树之前,还会有一次判断,只有键值对数量大于 64 才会发生转换,否则优先扩容。
还有一个就是扩容方法将头插法改成了尾插法避免了死锁,单在多线程环境下仍然是不安全的,多线程建议使用CurrentHashMap。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值