一、底层结构
数组+链表+红黑树
二、扩容
1、条件
元素个数超过数组长度✖️负载因子(默认0.75)
数组长度小于64,链表长度大于8
2、过程
1.7前头插法在多线程下容易死循环(环形链表)
1.8后改为尾插法
3、元素迁移
java1.8之后
1、元素e,hash值,数组下标(索引)
2、hash值&(n-1)
扩容前后的数组下标计算
0101为5,10101为21
又21=5+16;
3、扩容后的索引值
在扩容时
不需要重新计算元素的hash进行元素迁移,
用原先位置key的hash值&旧数组的长度。
如果结果为0,桶位置不变。
如果结果不为0,桶位置就是当前元素原来的
数组下标+旧数组长度。
4、总结
扩容前后的索引下标相差一个Bit位,可知:
若hash值&扩容前数组长度为0,则扩容后的数组下标不变。
若不为0,则扩容后的数组下标为扩容前的数组下标+扩容前的数组长度。
三、链表转红黑树
1、条件
数组长度大于64,链表长度大于8
2、红黑树特征
根节点都为黑色
每个节点,到其叶子节点的所有路径,包含相同数目的黑色节点
3、优点
相比于二叉查找树,可以避免单一链表,
增加查找效率。
四、多线程下存在的问题
1、put数据覆盖。
2、put导致扩容,再get导致为null。
3、扩容1.7时头插法会出现环形链表,死循环;1.8采用尾插法。
五、线程安全使用concurrenthashmap