HashMap
HashMap 1.7到1.8之间的变化
- HashMap 1.7底层是数组+链表 1.8 是数组+链表+红黑树 加入红黑树提高了查询效率 和插入效率
- 1.7链表使用的是头插法、1.8使用的是尾插法 因为1.8插入元素时要判断元素的个数,这就要遍历链表统计元素的个数,所以正好使用尾插法
- 1.7中的哈希算法比较复杂,包括右移异或运算,1.8进行了简化,因为复杂的哈希运算的目的时提高散列性,提高效率,而1.8提供了红黑树,所以可以适当简化哈希算法,节省cpu
HashMap的put流程
- 通过key进行哈希运算与数组长度取模得到数组小标
- 如果数组小标元素位置为空,则将key-value封装成entry对象(1.8是node)放入该位置
- 如果该元素位置不为空,则分情况讨论
a.如果是JDK1.7
则先判断是否需要扩容,扩容就直接扩容,不需要扩容就生成entry对象,头插法放入链表中 如果扩容 就是先生成新数组,然后遍历老数组每个位置上链表的每个元素,取key然后计算,然后将新元素放入新数组,然后将新数组赋予hashmap的table属性
b.如果是JDK1.8
则先判断当前位置是链表结构还是红黑树结构
如果是红黑树,则放入对应位置,其中会判断是否存在该key 存在则更新value
如果是链表,则遍历元素,这个过程会比较key,如果没有相同的则采用尾插法插入链表末端,然后在判断是否需要转化为红黑树
最后判断时候需要扩容
如果需要 扩容流程是
先生成新数组,遍历每个链表和红黑树上的每个元素,计算其在新数组中对应的位置
然后计算每个位置对应的元素个数,超过8则构建红黑树,将头节点放入数组中,否则将链表头节点放入 最后再赋予table属性
ConCurrentHashMap的扩容机制
首先分两个版本讲
java 1.7-------这个时候ConCurrentHashMap用的还是segment实现
- 每一个segment还相当于一个小型的HashMap
- 每个segment都会进行扩容,扩容逻辑和Hashmap类型
- 先生成新的数组,再将元素转移到新数组中去
- 扩容也是每个segment内部单独判断的,是否超过阈值
java 1.8-------这个时候ConCurrentHashMap不再基于segment实现
- 当put时,如果正在进行扩容,那么该线程一起扩容
- 没有扩容则正常添加到ConCurrentHashMap其中,然后判断是否超过阈值,超过则进行扩容
- ConCurrentHashMap支持多线程同时扩容
- 扩容前也是先生成一个新的数组
- 转移时,先将原数组分为不同的组,将每组交给不同的线程来负责元素的转移,每个线程负责一组或多组的元素转移工作