/** * 经常使用方法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; }