1.7版本
1. 1.7版本的ConcurrentHashMap是基于Segment(色们)分段实现的
2. 每个Segment相对于⼀个⼩型的HashMap
3. 每个Segment内部会进⾏扩容,和HashMap的扩容逻辑类似
4. 先⽣成新的数组,然后转移元素到新数组中
5. 扩容的判断也是每个Segment内部单独判断的,判断是否超过阈值
1.8版本
1. 1.8版本的ConcurrentHashMap不再基于Segment实现
2. 当某个线程进⾏put时,如果发现ConcurrentHashMap正在进⾏扩容那么该线程⼀起进⾏扩容
3. 如果某个线程put时,发现没有正在进⾏扩容,则将key-value添加到ConcurrentHashMap中,然
后判断是否超过阈值,超过了则进⾏扩容
4. ConcurrentHashMap是⽀持多个线程同时扩容的
5. 扩容之前也先⽣成⼀个新的数组
6. 在转移元素时,先将原数组分组,将每组分给不同的线程来进⾏元素的转移,每个线程负责⼀组
或多组的元素转移⼯作
Jdk1.7到Jdk1.8 HashMap 发⽣了什么变化(底层)?
1. 1.7中底层是数组 链表,1.8中底层是数组 链表 红⿊树,加红⿊树的⽬的是提⾼HashMap插⼊
和查询整体效率
2. 1.7中链表插⼊使⽤的是头插法,1.8中链表插⼊使⽤的是尾插法,因为1.8中插⼊key和value时
需要判断链表元素个数,所以需要遍历链表统计链表元素个数,所以正好就直接使⽤尾插法
3. 1.7中哈希算法⽐较复杂,存在各种右移与异或运算,1.8中进⾏了简化,因为复杂的哈希算法的
⽬的就是提⾼散列性,来提供HashMap的整体效率,⽽1.8中新增了红⿊树,所以可以适当的简化
哈希算法,节省CPU资源
说⼀下HashMap的Put⽅法
先说HashMap的Put⽅法的⼤体流程:
1. 根据Key通过哈希算法与与运算得出数组下标
2. 如果数组下标位置元素为空,则将key和value封装为Entry对象(
JDK1.7中是Entry对象,JDK1.8中是Node对象)并放⼊该位置
3. 如果数组下标位置元素不为空,则要分情况讨论
a. 如果是JDK1.7,则先判断是否需要扩容,如果要扩容就进⾏扩容,如果不⽤扩容就⽣成Entry
对象,并使⽤头插法添加到当前位置的链表中
b. 如果是JDK1.8,则会先判断当前位置上的Node的类型,看是红⿊树Node,还是链表Node
ⅰ. 如果是红⿊树Node,则将key和value封装为⼀个红⿊树节点并添加到红⿊树中去,在这个
过程中会判断红⿊树中是否存在当前key,如果存在则更新value
ⅱ. 如果此位置上的Node对象是链表节点,则将key和value封装为⼀个链表Node并通过尾插
法插⼊到链表的最后位置去,因为是尾插法,所以需要遍历链表,在遍历链表的过程中会
判断是否存在当前key,如果存在则更新value,当遍历完链表后,将新链表Node插⼊到链
表中,插⼊到链表后,会看当前链表的节点个数,如果⼤于等于8,那么则会将该链表转
成红⿊树
ⅲ. 将key和value封装为Node插⼊到链表或红⿊树中后,再判断是否需要进⾏扩容,如果需要
就扩容,如果不需要就结束PUT⽅法