数组+链表+红黑树,线程不安全
当表长小于64时,不转换为红黑树,数组长度翻倍扩容
当同一hash下元素小于8,不转换为红黑树
加载因子0.75,默认大小16.
- putVal()函数:
条件 | 操作 |
---|---|
表是空表 | resize()初始化表 |
新元素没有与旧元素碰撞 | 存入新元素 |
新元素与旧元素碰撞 | 见下表 |
走的前两步的话就判断是否需要扩容 | resize() |
条件 | 操作 |
---|---|
新元素与旧元素的key相同 | 记录这个位置 |
旧元素已经是红黑树节点了 | 在红黑树中插入新元素 |
新元素与旧元素key不同,且旧元素不是红黑树节点 | 在链表尾部插入新元素,如果插入后元素达到8个时,如果原数组长度达到64,就转换为红黑树,否则resize()。在遍历到尾部的过程中如果遇到相同key的元素就记录这个位置并直接跳出这个循环 |
(此步用来处理键值对相同时是否替换为新元素的情况)记录的位置不空&&(允许修改||这个位置的值为空) | 记录新元素并返回旧元素 |
- resize()函数:
条件 | 操作 |
---|---|
原表容量>0(正常扩容) | 没超过最大值就翻倍,否则设置为最大值 |
原表容量<=0且原阈值>0(初始化) | 新表容量为原阈值,新阈值为新容量*加载因子0.75或整数最大值 |
原表容量<=0原阈值<=0 | 使用定义的默认值 |
然后将原表中的每一条链表分为两条,hash&旧容量为0的放在新表原位置,不为0的放在旧表容量+原位置
-
hash()函数:
- (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)【扰动函数】
h>>>16: int为32位,>>>为无符号右移,取出该hash值的高16位
^: 异或,&更趋近于0,|更趋近于1,使用^可以更加随机
使用hashcode与其高16位异或:
由于绝大多数情况下length一般都小于2^16即小于65536。所以return h & (length-1);结果始终是h的低16位与(length-1)进行&运算。
因此可以将hashcode的高16位也利用起来,使最终得到的结果更加散列。
-
indexFor(h & (length-1))(1.8中没有,但原理相同)
-
h为上一步hashcode计算后的值
-
length为数组长度
对length取余
-
红黑树
插入时:
- 父节点为黑色:直接插入。
- 父节点为红色:
- 叔叔节点为红色:直接插入,将父节点和叔叔节点变黑,祖父变红,向根回溯,直到根并将其设置为黑
- 叔叔节点为黑色:
- 左左(右右):将父节点作为祖父节点,原祖父节点作为原父节点的右(左)孩子,原父节点的右(左)孩子作为原祖父节点的左(右)孩子,原祖父节点的右(左)孩子不变,然后原祖父变红,原父节点变黑。
- 左右(右左):将当前节点作为父节点,原父节点作为当前节点的左(右)孩子,然后进行左左(右右)旋转。
删除:红黑树删除节点
个人理解:红黑树相当于变种的平衡二叉树,通过颜色来保证树的大致平衡。