美文网首页
Java集合之HashMap底层实现原理[1]

Java集合之HashMap底层实现原理[1]

作者: walton_wang | 来源:发表于2020-06-06 16:35 被阅读0次

此篇博客源码是基于jdk 1.7

主要讲的知识点罗列如下

 1、底层结构

 2、扩容机制

 3、线程安全问题


底层结构及hash冲突

hashMap底层结构:数组+链表

Entry对象的数组 Entry链表

Entry中保存着HashMap的key和value;next对象是当出现hash碰撞时,用于存储原该entry链表中对象值;

保存新的key,value

为什么会出现hash冲突?

图中标注:通过hash获取bucketIndex

如图中标注:通过key.hashcode()计算出hash值,如果存储的对象多了,就有可能出现不同的对象计算出来的hash值相同,这时hash冲突就出现了;

如何扩容的呢?

hashMap在构造对象的时候,如果不指定构造参数,会有默认初始值;初始容量为:16,默认加载因子:0.75f;当然啦你也可以自定义初始值,一般在知道容器大小的时候,建议给个初始值大小;

里面有个关键字段:threshold(阈值);这个用来计算何时扩容,threshold=Math.min(capacity *loadFactor, MAXIMUM_CAPACITY +1)

扩容判断,增大原来2倍

扩容后重新根据key计算hash,获取对应的bucketIndex;

是线程安全的吗?如何保证线程安全?

不是线程安全的,主要原因会出现死循环上;

线程A,图片来自网络

多线程环境下,线程A当对容器进行扩容的时候,执行到如图所示位置时,线程A时间片被消耗完,线程被挂起;此时: e=3,next=7,e.next = null;

线程A被挂起后结构图

此时线程B获得时间片,执行扩容后完成数据的转换;

线程B正常执行完扩容后

此时线程A获取cpu时间片,继续执行剩下的步骤,newTable[i] = e;e = next;执行后如图:

继续执行下一轮循环,此时e=7,从主内存中读取数据发现e.next=3;然后继续此轮循环,结果如下:

然后继续执行下轮循环,此时e = 3,e.next = null; 当执行到e.next = newTable[i]时,e.next = 7,采用头插法后,结果如下,3和7之间形成环形;此时循环结束,此时还不会有死循环的危险;

当容器进行下一轮的扩容或者通过key获取值时等操作时,会发生死循环的危险;

怎么避免线程不安全呢?

1、可以使用hashtable;

2、通过Collections.synchronizedMap() 方法包装;

3、使用ConcurrentHashMap (推荐使用)

总结:HashMap在计算初始容量的时候,如果给了初始值,其实在内部会做个转化,使其容量为2的整数倍;当size>=threshold时,会执行扩容,倍数为当前容量的2倍;在使用的时候要避免出现线程安全的问题,存在并发的时候,使用ConcurrentHashMap来代替;

相关文章

网友评论

      本文标题:Java集合之HashMap底层实现原理[1]

      本文链接:https://www.haomeiwen.com/subject/azwxgttx.html