HashMap 学习
概念
- 比值
- 比较 origin 和 target 两个对象的类型
- 看看 origin 和 target 两个对象 hashCode 的 override 是否有实现,实现了就比较这个。
- 如果没有实现 hashCode 的 override 就比较 origin 和 target 两个对象的默认 hashCode。
- 负载因子
- 理论为 0.75
- 当为 Node 数组时,如果 length >= 16 * 0.75 时还不扩容,可能会 hash 碰撞,因此必须扩容为 16 * 0.75,以此类推。
- Node
- 单向链表相关
- 当 Node 数组下某个单向链表的 size >= 8 时,需要变为 TreeNode 相关的红黑树,不然查找性能会有损耗
- TreeNode
- 红黑树相关
- 当 TreeNode 节点少于 6 时,变为 Node 数组,如果不变的时候,后续操作会有损耗
put
- 按照最短路径优先找到叶子节点(既没有左孩子又没有右孩子),将当前 inputValue 放到该放的位置,也就是把 inputValue 变成叶子节点的左孩子或右孩子,并且此时 inputValue 根据红黑树规则必为红色。
- 检查 inputValue 和它的 parantValue 的颜色是否都为红色,根据红黑树规则不能有两个红色的 TreeNode 相邻,因此需要检查所有节点,进行节点的变色。
- 每次执行 put 逻辑到上一步之后,都会根据平衡逻辑进行所有节点的平衡,这是为了避免瘸腿的情况出现,导致方法 get 的性能变差,也就是所谓的红黑树变形 1。
- 在上一步的变形后,将真正的根节点再指回 Node 数组的第一个下标。
- 至此,一个红黑树构建完成。
get
- 查询的时候,就比较简单了,就是根据最短路径优先找到就是了。
总结
首先
你可以理解 HashMap 是个 Node 数组 + 单向链表,咱们每回 put 操作的 <K, V> 在 HashMap 看来应该根据它的算法 2 进行分门别类为Node节点的存放,例如,<A,“Str1”> 和 <AA,“Str2”> 在它看来可能都会归为 “某A” 这一堆,然后在 "某A"这个 Node节点下面,跟着 <A,“Str1”> 、<AA,“Str2”>。这是因为 “某A” 这一关键字,HashMap 会根据特定算法 2 ,将 K 们计算之后得到的值是相等的,所以都会放到 “某A” 下面。大概如下面这幅图:
“某A” | “某B” |
---|---|
<A,“Str1”> | … |
<AA,“Str2”> | … |
… | … |
请记住 Node [“某A”, “某B”, “…”],这里的 Node 数组是由图里的第一行组成的,可以认为 在每一个单个链表元素少的情况下 3 的 HashMap 是由上图构成的。
之后
在 在某个单个链表元素多的情况下 4 的 HashMap,可能存在某个链表太长了,导致执行 get 时候的效率低下,需要将 Node 从数组转换逻辑结构为红黑树去解决效率问题,至此,有了上述 put 的相关逻辑。在变为 TreeNode 数组描述的红黑树后,在每个 TreeNode 下面会跟着一个单向链表,每次最短路径优先算法,先找到的是分门别类后的 TreeNode ,再遍历下面的单向链表里面的 <K, V>。
概念修正在这里哦 ~
HashMap 上面说的 Node 数组 + 单向链表 不是十分正确的,后来仔细想了下,它就是个 二位数组 而已。大家想想看,如果有一个二位数组,我们的 key 可以根据上文中说的,会根据某个算法转换为一个数字,把这个数字作为下标,之后我们每回进行查询的时候,就会先直接命中外层数组中的下标,再命中内层数组的下标,可以拿到我们要的 Value 了。