JDK1.7中:
HashMap:
hashMap的实现就是通过数组加链表的形式组成的,初始时的容量为16,0.75*16
通过key对数组的长度进行取模计算,然后将entry挂在数组的位置上。
为什么要扩容:
多个entry在链表的时候,需要进行遍历,可知链表的遍历总是比较慢的
扩容时机:
当hash值发生冲突的时候,多个key相同,同时 threshold = loadFactor * 容量(大于数组的长度乘以加载因子的值(默认情况下数组长度是16*0.75=12时))且此时突然需要增加一个entry的时候。
线程不安全:
就是出现在扩容的时候,会调用一个transfer(转移数组)的扩容方法:
详情见:https://www.cnblogs.com/somelog/p/9299056.html
currentHashMap:
- 利用锁机制来进行同步操作,利用segment方式(即进行部分的划分)来进行,默认segment的长度为16,即一个currentHashMap最多有16个线程在进行操作,且必须是操作不同的segment才行。因此这决定了找到一个key值的时候,需要进行两次的hash定位操作,首先定位到segment,在定位到具体的key。
不同之处:
- 一个线程安全一个线程不安全
- hashmap运行key和value为null,currenthashmap不允许
- hashmap不允许在iterator遍历的情况下修改值,而currenthashmap可以
JDK1.8中:
hashmap当链表的元素查过了8个,就会将链表转换为红黑树(看自己的博客)的数据结构。
currentHashMap数据结构也是增加了红黑树,但是锁机制不在是采用segment的方式(但保证了segment的序列化方式),而是采用CAS(Compare-and-Swap)操作保证数据的获取以及使用synchronized关键字对相应数据段加锁实现了主要功能,这进一步提高了并发性。
CAS:
CAS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B。
CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。整个比较并替换的操作是一个原子操作。