HashMap、Hashtable和ConcurrentHashMap常见面试题汇总

Hashtable和ConcurrentHashMap区别

1. 锁粒度不一样。Hashtable是 synchronized 全局一把锁;ConcurrentHashMap在1.7及之前版本中采用Segment分段锁,将一个ConcurrentHashMap分成16段分别上锁,在1.8及之后版本并发控制使用 synchronized 和 CAS锁 来操作。

2. 底层实现不一样。Hashtable底层采用数组 + 链表实现;ConcurrentHashMap在1.7及之前版本中采用分段数组 + 链表实现,在1.8及之后和HashMap一样,采用了数组+链表+红黑树的数据结构来实现。

 

什么是CAS乐观锁

CAS操作包含三个操作数—— 内存位置的值(V)、预期原值(A)和新值(B)。操作前,会将内存里面的V复制一份到自己的A里面,之后比较A和V的值是否一致,如果一样就把B赋值给V。如果发现之前储存的A和现在的V不一样了,就重复操作。但是这种自旋锁操作可能存在CPU消耗过大的可能,另外无法解决ABA问题(虽然大多数ABA情况对于我们影响并不大)。

 

HashMap在1.7和1.8版本有什么区别

1. 数据插入方法不同,在hash冲突的时候,1.7版本采用头插法,1.8版本采用尾插法。因为1.7版本对于冲突处理是链表结构,头插法速度更快,虽然这有可能存在逆序和死循环的风险。1.8因为采用红黑树,也就选择了尾插法,这也避免了逆序和死循环问题。

2. 扩容后,数据储存位置的计算方式不一样。在扩容后,扩容前的位置一定是原来的下标或者原来下标 + 扩容大小(比如原来 8 个桶,扩大到16个桶,原来在第一个桶里面现在可能在第1个或者第9个桶)。在JDK1.7的时候是直接用hash值和需要扩容的二进制数进行& 运算,而在JDK1.8的时候直接用了JDK1.7的时候计算的规律,也就是扩容前的原始位置+扩容的大小,提高了运算速度

3. JDK1.7的时候使用的是数组+ 单链表的数据结构。但是在JDK1.8及之后时,使用的是数组+链表+红黑树的数据结构。

 

ConcurrentHashMap在1.7和1.8版本有什么区别

ConcurrentHashMap线程安全的,他在两个版本主要区别在于:

1. 数据存储结构不同:1.7中采用Segment + HashEntry 的方式进行实现,lock加在每一个Segment上面;1.8中采用Node+ CAS + Synchronized 来保证并发安全性。

2. 计算size方法不一样:1.7中计算是不给Segment加锁,练习计算元素个数,最多计算三次,如果前后两次计算结果相同,则认为元素个数准确。如果前后两次计算结果都不一样,则给每一个Segment加锁,在计算一次;1.8中采用一个volatile类型的变量baseCount记录元素个数,每一次插入或删除数据都会更新,从而得到大小。

 

HashMap 的长度为什么是2的幂次方

因为在hashMap计算hash值的时候,可能数字很大远大于数组长度,就需要取hash值的余数作为索引位置,取余操作中如果除数是2的幂次则等价于其除数减一的与操作,也就是说hash%length=hash&(length-1),后面这种与操作运算更快。所以HashMap底层采用 (n - 1) & hash 取余操作,但也同时限制了长度必须是2的幂次方

 

HashMap的负载因子为什么是0.75

作为一般规则,默认负载因子(0.75)在时间和空间成本上提供了很好的折衷。较高的值会降低空间开销,但提高查找成本(体现在大多数的HashMap类的操作,包括get和put)。设置初始大小时,应该考虑预计的entry数在map及其负载系数,并且尽量减少rehash操作的次数。如果初始容量大于最大条目数除以负载因子,rehash操作将不会发生。

(来自源码对于0.75注释的翻译)

 

HashMap和Hashtable的区别有哪些

HashMap和Hashtable都实现了Map接口,因此很多特点十分相似,但是也有所不同:

1. HashMap允许值或者键为null,Hashtable不允许键或者值为null

2. Hashtable是线程安全的,HashMap线程不安全,所以HashMap更适合用于单线程环境

Hashtable已经基本上废弃了,所以一般认为Hashtable是一个遗留的类

 

快速失败和安全失败有什么区别?

Java.util包下所有集合都是快速失败的。如果在遍历集合的时候,有另外一个线程正在对集合进行操作,比如删除或添加,遍历会直接报错ConcurrentModificationException异常。java.util.concurrent包下集合都是安全失败的,遍历的时候会复制一份遍历复制的,他们永远都不会抛出这样的异常

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员麻薯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值