HashMap、ConcurrentHashMap总结

HashMap

我们都知道hashmap 的底层数据结构是数组+链表的node,node的key值通过一系列hash得到一个hashcode,根据hashcode放入数组某个位置。

当这个hashcode之前不存在时,就new一个节点放入数组;

当hashcode已经存在时,这时有两种原因:

1、一种是hashcode冲突了,也就是两个不同的key算出来了一个hashcode,这时就把节点放在链表后面;

2、另一种原因是hashcode一样,key也相同(源码通过==加equals判断),就把原key值的旧数据替换成新值。

说两句题外话,从这里我们可知两个对象的hashcode相同,这两个对象不一定相同,但是两个对象相同,hashcode一定相同。

并且除了String类等包装重写了equals,使得equals方法变成对象内容的比较。原来Object对象的equals方法就是==,比较对象的引用值是否相同。

言归正传,jdk1.8对hashmap和concurrenthashmap做了很大改进,先讲hashmap,对hashmap的改进是hashmap出现了大量的hash冲突,也就是当链表长到一定程度,这里默认是超过8链表就会变为红黑树,查找效率从O(n)变成了O(logn)。而当红黑树节点小于6之后又会变回链表。

 

ConcurrentHashmap

接下来说concurrentHashmap,1.7的concurrentHashmap是通过分段锁来实现的,就是针对每一个Entry,分为16个段,当put时先找到对象的段位置,再对段进行加锁,这里的加锁是Reetranlock,注意concurrentHashmap的get方法是不加锁的,因为get的value是volatile的,保证了最新以及可见性。1.8之后的concurrentHashmap抛弃了分段锁,通过CAS+synchronized来实现,当节点第一次加入数组时,通过CAS来保证数据的原子性。其余时候通过加synchronized锁来进行同步,1.8的concurrentHashmap也会进行树化。

CAS算法的过程是,它包含三个参数(V,E,N),V表示要更新的变量,E表示预期值,N表示新值,仅当V的值和E相等时,才会把V的值设为N。如果V值和E值不同,则说明已经有其他线程作了更新,则当前线程什么都不做,返回当前V的真实值。CAS是抱着乐观的态度进行的,它总是认为自己成功完成操作,当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其他都会失败。失败的线程不会被挂起,而只是被告知失败,并且允许重试,当然也允许放弃操作。

 

hash算法

hashmap中的hash算法是先将key的hashcode和无符号右移16位的hashcode进行异或,hashcode是int总共32位,也就是将hashcode的低16位和高16位进行异或,hash = (h = key.hashcode) ^ (h>>>16),再用node元素长度(n-1)&hash得到index下标,当rehash的时候只是n增加了一位,hash值并没有变化,因此index新增了1位0或者是1,这个设计十分巧妙,既省去了重新计算hash值的时间,而且同时,由于新增的1bit是0还是1可以认为是随机的,因此resize的过程,均匀的把之前的冲突的节点分散到新的bucket了。

 

参考http://yikun.github.io/2015/04/01/Java-HashMap%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E5%AE%9E%E7%8E%B0/?nsukey=Cy%2BdgYYGHZbOtrVEJOlmKzXmrri1njLiBPP7qB0MJ3w0Uxnf5eA0YbsfbtkjMw3u7yKOafmp6R0U373AVDws0MCFbMyI0RbTwqXJeJ%2Fg3uCdzWUFsrn3CcpyVr4RXcdtrEjxGBLZNxiYUIZgHzpGYpBEaq0GvA8o0c3S7nRImAILnfVcJpk0GqiYj75w9zq6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值