hashmap hash冲突怎么解决_HashMap原理

HashMap的问题,在现在的技术面试中,基本上都会问到,而且这个集合在开发中也会经常使用到,希望通过本文能有所收获。

一、问题分析

1、HashMap的底层数据结构是什么?

2、HashMap中CRUD操作的底层实现原理是什么?

3、HashMap是如何实现扩容的?

4、HashMap的hash冲突是怎样解决的?

5、HashMap为什么线程不安全?

二、认识HashMap

HashMap最早出现在jdk1.2中,一直到jdk1.7都没有太大的变化。但是到了1.8版本的时候,突然进行了一个很大的改动,最显著的就是:变成了数组+链表+红黑树。当链表长度超过阈值8的时候转为红黑树,这样能大大的减少了查找时间。之前版本中都是数组+链表的结构。另外HashMap是非线程安全的,也就是说在多线程中,多个线程对HashMap进行增删改操作的时候,不能保证数据的一致性。

三、深入分析HashMap

1、底层数据结构

95ee8a9c796618d4cf8149cdc19ff539.png

如图,是jdk1.7版本中的存储结构图。从图中能看出,首先把元素放在一个个Entry数组里面,然后存放的数据元素越来越多,就出现了链表,对于数组中的每一个元素,都可以有一条链表来表述存储元素。这就是有名的“拉链式”存储方法。

后来,存储的元素越来越多,链表越来越长,当查找一个元素的时候效率不仅没有提高,反而下降了,于是就进行了改进。就是把链表变成了一个适合查找的树形结构,红黑树,所以HashMap的存储数据结构就变成了下面这样:

dbd4e8e0a4f9e14966759e2c9457c302.png

其实就是把链表结构变成了红黑树,原来的优点是增删操作效率高,现在查找的效率也大大提高了。

注意:只有在链表的长度不小于8且数组长度小于64的时候才会将链表转成红黑树。

什么是红黑树?

红黑树是一个自平衡的二叉查找树,在每个节点增加一个存储位表示节点的颜色,红色或者黑色。通过任意一条从根到子叶的路径上各个节点颜色的限制,红黑树确保没有一条路径会比其他路径长出两倍,因此红黑树是一种弱平衡二叉树。查询效率非常高。

为什么非要等到链表长度大于等于8的时候才转变为红黑树,而不是直接变为红黑树?

1、因为构造红黑树要比构造链表复杂,另外在链表的节点不多的时候,数组+链表+红黑树的结构不一定比数组+链表的结构性能高。

2、HashMap扩容的时候,会造成红黑树不断的进行拆分重组,这是非常耗时的。所以,在链表长度比较长的时候才转变为红黑树,这样才会提高效率。

2、存储元素

在存储一个元素的时候,大多都是使用这种方式:

HashMap(String,Integer) map = new HashMap();

map.put(“张三”,22);

第一个是键,第二个是值,总称为键值对。那么底层是实现原理是咋样的呢?看下图:

d1c3b0be6eb39c202fe49ce4ae9b6648.png

第一步:调用put方法传入键值对。

第二步:使用hash算法进行计算。

第三步:根据hash值确定存放的位置,并判断是否和其他键值对发生了冲突。

第四步:如果没有冲突,直接存放到数组中。如果有冲突,要判断此时的数据结构是什么。

第五步:如果是红黑树,就直接插入到红黑树中。如果不是红黑树,要判断链表长度是否大于等于8。

第六步:如果大于8,那就先转为红黑树,然后再插入,如果不大于8,那就直接插入到链表尾部即可。

3、扩容

流程图如下:

a3074f4781ad0e29af439745509fccf8.png

扩容比较简单,就是先计算新的hash表容量和新的容量阈值,然后初始化一个新的hash表,将旧的键值对映射到新的hash表里,如果涉及到红黑树,那么映射的过程中还涉及到红黑树的拆分。

4、解决hash冲突

Hash冲突是再计算hash值时候出现了重复,HashMap中计算hash值就是通过hashcode与16异或计算来的。

bf5eb7e724edbfdd55765cb8d71d2d3b.png

如图,通过异或运算计算出来的hash比较均匀,不容易出现冲突,但是总有一些例外,一旦出现了冲突现象怎么解决呢?

再数据结构中,处理hash冲常用的办法有:开发定址法、再哈希法、链地址法、建立公共溢出区。而HashMap处理hash冲突的方法就是链地址法。

这种方法的基本思想就是将所有的哈希地址为i的元素构成一个称为同一次链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行,链地址法适用与经常进行插入和删除的情况。

72a7d546af831b55929f7b0c0d612961.png

如图,出现冲突的时候,一个接一个拍成一条链就可以了。刚好与hashmap的底层数据结构相呼应。

5、HashMap为什么是非线程安全的?

因为源码里面的方法全部独是非线程安全的,根本找不到synchronized关键字,所以保证不了线程安全。于是出现了ConcurrentHashMap。

上述资料不能面面俱到,如有遗漏,欢迎指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值