HashMap

本文探讨了HashMap的resize机制、线程不安全性与fail-fast机制、ConcurrentHashMap在Java 1.8中的实现及为何容量要为2的幂。通过具体实例说明了在扩容过程中元素如何重新定位,以及不同版本HashMap的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文不介绍HashMap的主要知识,而是

1.resize

2.线程为何不安全与fail-fast机制

3.concurrenthashmap在1.8的实现

4.容量为何要2次幂

 

resize

我以前认为是全部重新落桶, 其实深入思考下,不是这样的

他的hashcode比如010101010

不考虑resize,在put的时候会先(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)

进行扰动

比如扰动成1010101010

在真正执行put的时候,如果容量为32位,会取最后5个数

扩容了就多取一位

这个时候,数组上冲突的元素,有一些多取的那一位是0,有一些是1,如果是0,意味着这个节点的取的值不变,所以在数组中的位置不变,但是如果是1,就需要增加扩容前容量的长度

对于1.7的链表,就是简单的移除,然后增加到另一段链表上,如果有的话

对于1.8的红黑树,需要移除一个节点,然后维持平衡,然后增加到另一段链表/红黑树上, 然后维持平衡,如果有的话

 

线程为何不安全

在resize的时候,两个线程会创建两个数组,然后执行了一些逻辑,使得某数组的链表上形成了循环链表,最后也使用了这个数组作为真正采用的数组。然后在后来的put值的时候,遍历到了链表,然后在node.next的时候陷入了无限循环。

fail-fast

使用迭代器的时候,内部维护修改次数,如果不符合,就直接抛出异常。

 

ConcurrentHashMap

还是不想了解太多,1.8cas+锁单个数组位上的东西。

 

容量为何为2次幂

原因有多方面

1.2倍适中,既不频繁扩容,也不浪费空间

如果你保守地新增了数组的长度,面对数据量快速增长的情况下,扩容会变得十分频繁,2倍是非常省力的。但是为什么不是3倍?4倍?这样一来又显得浪费。能不能处于2倍之下的1点几倍?下面会解答

2.位运算(核心)

当你计算出来hashcode,正常思维是对数组长度取模,但是这样运算效率太低了!位运算可以直接截取后面若干位,直接得到结果。并且位运算可以很方便地进行扰动。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值