HashMap1.8中的put方法直接调用 putVal()方法,所以本文主要注释了 putVal()方法里面的代码。
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i; //定义存放元素的节点数组,节点,变量n,i;
if ((tab = table) == null || (n = tab.length) == 0) //把成员变量table赋值给tab,如果table、tab为空,把tab初始化,长度n为初始化后数组的默认长度16
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null) //当前节点存放在数组中,没有存放到红黑树和链表中,n-1=15,(n - 1) & hash能得到数组的下标位置,如果tab[i]==null,说明这个位置可以存放元素p
tab[i] = newNode(hash, key, value, null); //在tab[i]处新建节点存放元素(hash, key, value, null)
else { //tab[i]已经有存放的元素,说明hash冲突
Node<K,V> e; K k; //定义一个节点数组e和K k值;
/*
* 如果当前节点的hash值(p.hash)和参数列表中的hash值相等且当前节点的key和给定的key相等,则把当前节点赋值给e;
* */
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof HashMap.TreeNode) //如果当前节点是红黑树,则走红黑树的分支
e = ((HashMap.TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else { //当前节点是链表的情况,通过for循环遍历链表中的元素,把链表中的元素存放到map中,for()没有循环终止条件,只能在程序中break
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) { //如果当前节点指向的next为空,说明链表上之后一个元素,这个元素又在数组上存储,可以把这个元素存储到next节点
p.next = newNode(hash, key, value, null); //在链表的next的节点新建一个node,存储给定的值(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st 如果链表上节点数量超过二叉树临界点的值,链表转换为二叉树
treeifyBin(tab, hash);
break; //退出if(e = p.next) == null)
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) //如果next位置的元素和给定的元素是同一个值,则退出,把当前元素赋值给当前元素
break; //退出
p = e;
}
}
/*
* e中存储的是hash值和key值都和给定元素相同的节点:
* 参考【if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
* if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) //如果next位置的元素和给定的元素是同一个值,则退出,把当前元素赋值给当前元素
break; //退出
p = e;】
* 此时需要根据 onlyIfAbsent来确定是否替换value
*onlyIfAbsent:如果当前位置已存在一个值,是否替换,false是替换,true是不替换
*
* */
if (e != null) { // existing mapping for key
V oldValue = e.value; //把e节点的值赋值给oldValue
if (!onlyIfAbsent || oldValue == null) //根据条件判断是否替换
e.value = value; //替换
afterNodeAccess(e); //回调函数,把e节点移动到map最后
return oldValue; //不替换返回之前的值
}
}
++modCount; //记录HashMap被结构修改的次数
if (++size > threshold) //map长度大于扩容临界值,map扩容
resize(); //扩容方法
afterNodeInsertion(evict); //回调函数,新节点插入之后回调 , 根据evict 和 判断是否需要删除最老插入的节点。
return null; //返回
}