HashMap源码之put()方法
Posted heaven-elegy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HashMap源码之put()方法相关的知识,希望对你有一定的参考价值。
/**
* 常用方法put
*/
public V put(K key, V value) {
// 获取key的hash并命令putval方法替换现有值与非创建模式
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
// tab - 当前hash桶的引用
// p - key所代表的节点(此节点不一定是目标节点,而仅仅是hash与桶长度的计算值相同而已)(它不为空时可能是链表或红黑树)
// n - 当前桶的容量
// i - key在桶中的下标(同p,不代表目标节点)
Node<K,V>[] tab; Node<K,V> p; int n, i;
// 初始化局部变量tab并判断是否为空,初始化局部变量n并判断是否为0
// PS: 源码中大量的使用了这种书写方法,不知道放在某写大厂里会怎么样(斜眼笑)
if ((tab = table) == null || (n = tab.length) == 0)
// 当tab为空或n为0时,表明hash桶尚未初始化,调用resize()方法,进行初始化并再次初始化局部变量tab与n
n = (tab = resize()).length;
// 初始化p与i
// 这里使用了(n - 1) & hash的方式计算key在桶中的下标.这个在后面单独说明
// 当p是否为空
if ((p = tab[i = (n - 1) & hash]) == null)
// p为空,调用newNode方法初始化节点并赋值到tab对应下标
tab[i] = newNode(hash, key, value, null);
else {
// p不为空,发生碰撞.进行后续处理
// e - 目标节点
// k - 目标节点的key
Node<K,V> e; K k;
// 判断key是否相同.(这里除了比较key以外,还比较了hash)
// 注意,这里同时初始化了局部变量k,但是在第二组条件不满足的情况下,没有使用价值,可以被忽略
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
// key相同,将e(目标节点)设置为p
e = p;
// 判断节点是否是红黑树
else if (p instanceof TreeNode)
// 确定时,直接委派处理
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
// 走到这里,代表当前节点为普通链表,进行遍历查找
// 变量binCount只作为是否达到tree化的阈值判断条件.
for (int binCount = 0; ; ++binCount) {
// 获取链表的下一个元素,并赋值到e(此时e是一个中间变量,不确定是否是目标节点)
// 第一次for循环时,p代表hash桶中的节点(同时也是链表的头部节点),之后一直等于p.next
if ((e = p.next) == null) {
// 链表遍历到末尾
// 向链表中追加新的节点
p.next = newNode(hash, key, value, null);
// 判断当前链表长度是否达到tree阈值
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
// 调用treeifyBin方法直接处理
treeifyBin(tab, hash);
// 中断循环
// 注意,此时局部变量e=null
break;
}
// 能走到此处,说明链表未结束,比较e的k是否相同(hash与==)
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
// key相同
break;
// e既不为null也不是目标节点,赋值到p,住被进行下次循环
p = e;
}
}
// 判断e是否存在
if (e != null) { // existing mapping for key
// e不等于null说明操作为"替换"
// 缓存老值
V oldValue = e.value;
// 判断是否必须替换或老值为null
if (!onlyIfAbsent || oldValue == null)
// 必须替换或老值为空,更新节点e的value
e.value = value;
// 调用回调
afterNodeAccess(e);
// 返回老值
// 注意,这里直接返回了,而没有进行modCount更新与下面的后续操作
return oldValue;
}
}
// 除了更新链表节点以外,都会走到这里(putTreeVal的返回值是什么有待确认)
// modCount+1
++modCount;
// size+1(元素数量+1)
// 判断是否超过阈值
if (++size > threshold)
// 重置大小
resize();
// 调用后置节点插入回调
afterNodeInsertion(evict);
return null;
}
以上是关于HashMap源码之put()方法的主要内容,如果未能解决你的问题,请参考以下文章