详解 HashMap 源码解析(下)
上文介绍了
HashMap整体介绍了一下数据结构,主要属性字段,获取数组的索引下标,以及几个构造方法。本文重点讲解元素的添加、查找、扩容等主要方法。
添加元素
put(K key, V value)
首先算出 key 的哈希码,调用hash方法,获取到hash值。
调用 putVal()
首先判断哈希数组
table是否为null,如果为null,就扩容。(n - 1) & hash对应的下标是否存在节点。不存在节点,就创建新的节点并赋值。
存在节点
节点 key 值是否相等,相等就替换
value。是否为红黑树,添加数据到红黑树中。
上面都不符合,就是普通链表,遍历链表,如果链表存在相同
key就替换,否则在链表最后添加数据。
流程图:
putAll(Map<? extends K, ? extends V> m)
putAll 是将集合元素全部添加到HashMap中,putAll调用了putMapEntries方法,putMapEntries先判断是否需要扩容,然后遍历元素,调用putVal添加元素,下面是添加元素代码:
获取数据
get(Object key)
通过key找到哈希表的中Node节点的value值。
首先使用hash方法算出哈希值,然后再调用getNode()获取数据:
判断哈希数组是否不为
null并且数组下标(n - 1) & hash处不为null,如果都有值,就查询首节点first,否则返回null。找到首节点,匹配上相等的
hash和key,返回首节点。链表有多个元素,是否为红黑树
是红黑树,在红黑树查找
不是红黑树,就遍历普通链表,直到匹配到相同的
hash和key值。
流程图:
resize 扩容
当哈希数组为null,或元素个数超过了阈值,就调用resize扩容方法:
原容量是否为空
不为空,是否大于最大容量
大于最大容量,不做扩容
小于最大容量,并且大于默认容量 16。阈值和容量都翻倍。
为空,原阈值大于零, 就阈值赋值给新容量。
原容量和原阈值都小于等于零,赋值默认容量 16 和默认阈值 12。
做完阈值和容量的赋值之后,遍历数组。
有值,是否只有一个元素,如果是就放入新数组
n-1&hash下标处。如果是红黑树就拆分红黑树。
上面两个都不符合就是普通链表。
遍历链表,如果
hash&数组原长度为 0放在数组
原下标处。不为零,放在
原位置+原数组长度处。
流程图:
总结
本文主要讲解了元素的添加、查找、扩容等主要方法,其中添加和查询都需要先获取数组的下标,然后进行对应的操作。
put添加
首次添加数据需要对数组进行扩容。
对应下标是否有值
没有值,直接赋值
有值
key一致,替换value值。key不一致是红黑树,在红黑树添加数据。
不是红黑树,就是链表,遍历链表,存在相同节点 key,替换。否者添加在链表的尾部。
get查询
下标是否有值
没有值,返回
null有值*
hash和key相等的话,返回节点。是否是多链表。
不是,返回
null。是的话,是否是红黑树。
红黑树,在红黑树中查找
否则就是普通链表,遍历链表知道匹配到相同的
hash和key。
resize 扩容
容量大于零
大于最大容量值,不再扩容。
介于最大和默认容量之间,阈值和容量都翻倍。
初始化的时候,设置默认容量和默认阈值。
遍历原数组
节点有值,并且只有一个值,赋值给新数组
n-1&hash处。如果是红黑树,就拆分红黑树。
以上都不符合,就是普通链表,遍历链表。因为数组长度都是 2 的幂次方,扩容后元素的位置*要么是在原位置,要么是在原位置再移动 2 次幂的位置。
hash&与运算原数组长度,等于 0,存在原来的位置。
不等于 0,就存放下标原来位置+原数组长度位置处。









评论