概述
- java中的hash表。
- 主要分析jdk1.8版本
- 容量上限是int类型的上限值。
jdk1.7及之前版本的hashMap在并发场景下会有死循环问题。
put方法
插入或替换方法
- 判断存储数组是否初始化过
- 没初始化过则调用扩容方法进行初始化
- 找到对应hash表位置是否存在节点
- 不存在节点则直接新增,完成
- 存在节点则继续判断
- 该位置对应的节点是否就是要插入的节点
- 完全匹配了,则更新值,完成。
- 如果不能匹配,那这个节点的值可能是树节点或链表,继续向后判断。
- 判断这个位置的节点是否是树节点。
- 如果是树节点,则调用树节点的插入方法,完成。
- 以上都不是,那这个位置的节点只能是链表节点了。
- 在链表中匹配要插入的节点,匹配不到则插到链表最后,完成。
- 判断数组是否存在,不存在返回null
- 判断数组长度是否大于0,否则返回null
- 判断对应hash位置是否存在节点,不存在返回null
- 比对这个节点上的值是否是目标值,匹配成功直接返回。
- 节点上的值不是,则判断这个节点是否是链表节点或树节点(是否有next),都不是返回null。
- 判断是不是树节点,调用树节点的get方法,直接返回。
- 到最后一定是链表节点了,循环比对链表上的值,直接返回。
resize()方法
- 判断扩容前数组容量是否大于0
- 大于0表示正经扩容的,(就是原本数组有一定长度,这里不够了需要扩容)
- 正经扩容时先判断是否已超过某个极限值,如果超过了,则直接设置长度为int类型的最大值。
- 没超极限值则容量翻倍
- 如果长度在极限值以下,默认值以上则指定阈值。否则留给下一阶段指定。
- 数组长度不大于0说明还没初始化
- 大于0表示正经扩容的,(就是原本数组有一定长度,这里不够了需要扩容)
- 判断原数组阈值是否大于0.
- 就是如果HashMap创建时指定了初始化长度,则使用指定的长度进行初始化。
原数组没初始化,也没制定初始化大小,则使用默认值
因为创建HashMap时如果指定好了数组大小,则这里需要计算下阈值大小。
- 另外如果原长度为2,扩容长度到4,那就不能简单的把阈值乘2。
转移数据
- 只有原本有数据的才需要转移,如果是初始化原本没有数据,就不用这一步了
- 循环已有的数组:
- 节点不能为空。(为空的是还没插入过数据的)
- 判断节点的next是否为null
- 为null表示不是树节点也不是链表节点,直接转移到新数组即可。
- 如果存在nuxt则继续判断
- 节点是否是树节点。
- 是树节点则调用树节点的转移方法。
- 上面两个都不是则一定是链表节点。
- 链表节点转移只可能会落到新数组的两个位置,(由hash方法决定的)
- 直接计算高低位,处理成两个链表,直接赋值到新数组对应位置。
compute()方法
- 传入key和一个lambda表达式,
- 表达式传入的值是原节点的key和value,
- 如果原节点不存在或value没有会传null。
- 返回值是新的value值,如果为null会做删除操作。