JDK1.8中HashMap优化分析

HashMap底层实现是数组,这里分析下jdk1.8中对HashMap的优化

1. hash算法优化
	// jdk1.8 HashMap中hash源码
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

源码解读:
用 (key的hash值) 与 (key的hash值右移16位)进行异或运算

例如:

		HashMap<String,String> hashMap = new HashMap<>(); // 1
        hashMap.put("test1","测试数据1");	// 2
        hashMap.put("test2","测试数据2");	// 3

详细过程:

// 对第二行中的key进行hash:
// hash后:
h = 110251487
// 二进制的表示形式为:
0000 0110 1001 0010 0100 1101 1101 1111
// 对其右移16位:
0000 0000 0000 0000 0000 0110 1001 0010
// 进行异或运算:
0000 0110 1001 0010 0100 1011 0100 1101
// 转回二进制为:
110250829

右移16位:将高16位推到了低16位上,高16位用0来补齐
^表示异或:a、b两个值不同,则异或结果为1。如果相同,异或结果为0
&表与运算:两位同时为“1”,结果才为“1”,否则为0

优化的实质是将高16位与低16位进行异或运算(结果中与之前高十六位是一致)。
这么做的目的是:是希望低16位中同时保留低16位与高16位的特征,尽量避免key的低16位近似,以免发生hash冲突

2. 寻址算法优化

核心:hash & (n-1)
第一步中异或后的值 与 数组长度-1进行 &(与) 运算
0000 0110 1001 0010 0100 1011 0100 1101
0000 0000 0000 0000 0000 0000 0000 1111(假设数组长度16 ,15的二进制)
因为15的高16位都为0,所以与运算结果都是0,一般数组的值都比较小,所以核心点在低16位的与运算;

问题:为什么不再像JDK1.7一样进行取模运算?

答 :取模运算相对来说性能比较差一些,hash&(n-1) 与取模操作效果一样,但是与运算的性能更高

问题:为什么hash&(n-1)和hash值对数组长度取模效果一样?

答:数学上得到结论,当数组长度是2的幂次时,hash值对数组长度取模的效果和 hash&(n-1) 是一样的。

寻址算法优化的目的:用与运算代替取模,提升性能。

3. 解决hash碰撞问题

多个key的hash值,与 n-1 (数组长度减一) ,与运算之后,依旧会出现定位的位置相同问题,即 hash碰撞

解决方式为:
如果发现定位到的位置已有元素,则在该位置挂一个链表,在链表里放多个元素,用来解决hash碰撞问题。

在使用get方法时,如果发现该位置是一个链表,则遍历该链表,拿到该key对应的元素。

但是当链表的长度过长时,性能会下降,时间复杂度为O(n)

所以当检测到链表达到一定长度时(当链表长度大于8的时候转换为红黑树),将链表转化为红黑树,来提升性能,红黑树的时间复杂度为:logn

4. 扩容

当put时检测到数组满了,则进行扩容,扩容的方式是2倍扩容
进行rehash时,使用key的hash与新的数组长度-1进行与运算,判断结果的高位是否多出一个 bit 的 1,如果没多,那么就是原来的index,如果多了出来,那么就是index+oldCap(原来的index+原来数组的长度),通过这个方式,可以避免rehash时,用每个hash对新的数组.length进行取模,取模性能不高,位运算的性能比较高

5. jdk1.7中扩容后死循环问题

HashMap扩容导致死循环的主要原因:
在于扩容过程中使用头插法将oldTable中的单链表中的节点插入到newTable的单链表中,所以newTable中的单链表会倒置oldTable中的单链表。那么在多个线程同时扩容的情况下就可能导致扩容后的HashMap中存在一个有环的单链表,从而导致后续执行get操作的时候,会触发死循环,引起CPU的100%问题。所以一定要避免在并发环境下使用HashMap。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值