hashMap扩容机制1.7和1.8汇总及对比

不管是JDK1.7或者JDK1.8 当put方法执行的时候,如果table为空,则执行resize();方法扩容。默认长度为16;

JDK1.7扩容

条件:发生扩容的条件必须同时满足两点

  1. 当前存储的数量大于等于阈值
  2. 发生hash碰撞

特点:先扩容,再添加(头插法)

缺点:头插法会使链表发生反转,多线程环境下会产生环

扩容之后对table的调整

table容量变为2倍,元素下标需要重新计算
重新获取key的hashcode 再对hashcode进行扰动(具体解释可以翻我文章)
newinex = hash (扰动后) & (length - 1)

JDK1.8扩容

条件:当前存储的数量大于等于阈值

特点:尾插法,先插后判断是否需要扩容

缺点:多线程下,1.8会有数据覆盖
举例:
线程A:往index插,index此时为空,可以插入,但是此时线程A被挂起
线程B:此时,对index写入数据,A恢复后,就把B数据覆盖了

扩容之后对table的调整

table容量变为2倍,元素下标可以直接定位原节点在新的table中的位置,怎么计算呢?

由于table扩大为2倍,相当于低位掩码高位多了一个 1 ,例如原来length是16其length - 1 的二进制 是 0000 1111 ,扩大为32之后 length - 1的二进制是 0001 1111 ,再回到计算下标的公式 newinex = hash (扰动后) & (length - 1),扩大之后低位掩码高位多了一个 1 ,因为1 和 1与是1,和0与是0,所以这里的newindex 的值就取决于 hash(扰动后) 的倒数第五位的值。
(关于具体取index 的介绍可以翻我文章)

如果这个hash倒数第五位的值 是 0 与之后还是0 就和扩容前的值一样,所以下标不变;

如果这个hash倒数第五位的值 是 1 与之后是1 就比扩容前的值翻了一倍,所以下标是原下标的二倍;

关于链表转红黑树

当插入之后,判断链表的长度 > 8?且数组长度 > 64 都满足则转为红黑树;
否则判断是否大于阈值,大于则扩容。

  • 为什么这里是8?
    时间和空间的折衷,红黑树节点大小约为链表节点的2倍,在节点太少时,红黑树的查找性能并不明显,付出2倍的空间代价不值得。

对于移除,移除后达到6,且该节点是红黑树节点 则转为链表。

  • 为什么是6?
    当节点个数在8徘徊时,频繁的转换,性能损耗大。

猜你喜欢

转载自blog.csdn.net/KuKu_Nao/article/details/118391176