ConcurrentHashMap get和put方法解析(借鉴大神的理解)
原文出处 https://blog.csdn.net/liaob0106/article/details/91878302
首先把那些恶心的变量解释一下
变量名称 含义
bincount table里目标索引链表的元素个数
f table里目标索引对应链表的头结点
n table的长度
i 目标索引
fh 头结点f的哈希值
tab table数组的副本
final V putVal(K key, V value, boolean onlyIfAbsent) {
//1.判断key是否为空
if (key == null || value == null) throw new NullPointerException();
//2.计算哈希值
int hash = spread(key.hashCode()); int binCount = 0;
//3.得到table的数组
`for (Node<K, V>[] tab = table; ; ) {
Node<K, V> f; int n, i, fh;` //4.如果table数组为空,则初始化table ` if (tab == null || (n = tab.length) == 0) tab = initTable();` //5.如果对应key的哈希值上对应table数组下标的位置没有node,则通过cas操作创建一个node放入table中,然后putval出栈 `else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { if (casTabAt(tab, i, null, new Node<K, V>(hash, key, value, null))) break; // no lock when adding to empty bin }` //6.如果table正在扩容,则得到扩容后的table,然后再重新开始一个循环 ` else if ((fh = f.hash) == MOVED) tab = helpTransfer(tab, f); else {` //7.到这里说明找到了key hash后对应的table,并且table上有其他node的存在 ` V oldVal = null;` //8.把这个找到的node加上同步锁,防止并发出现的问题,如果其他key put进来的时候也对应这个tab则堵塞在这里 `synchronized (f) {` //9.再次用cas确认索引i上的table为我们找到的node,如果不是的话则这个node被修改,直接释放锁进入下一个循环 `if (tabAt(tab, i) == f) {` //10.如果目标table的第一个node的哈希值大于等于0,则是链式结构,走链表查找,反之走红黑树查找 ` if (fh >= 0) {` //11.标志bincount为1,因为在该table上至少有一个node节点 ` binCount = 1;` //12.循环链表 ` for (Node<K, V> e = f; ; ++binCount) { K ek;` //13.如果遍历元素的哈希值与需要插入目标key的哈希值相同,并且值也相同,则插入的是重复key的元素 ` if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { oldVal = e.val;` //14.如果onlyIfAbsent为false的话,则替换为新value,否则不修改(一般传false) ` if (!onlyIfAbsent) e.val = value;` //15.break循环 ` break; }` //16.循环直到最后一个node节点的key都不是我们想要插入的key ` Node<K, V> pred = e; if ((e = e.next) == null) {` //在尾部添加一个新节点,break循环 ` pred.next = new Node<K, V>(hash, key, value, null); break; } } }` //17.该节点属于红黑树的子节点,进行树操作 ` else if (f instanceof TreeBin) { Node<K, V> p; binCount = 2; if ((p = ((TreeBin<K, V>) f).putTreeVal(hash, key, value)) != null) { oldVal = p.val; if (!onlyIfAbsent) p.val = value; } } } }` //18.如果node节点不为0 ` if (binCount != 0) {` //19.如果node大于或者等于8,则转为红黑树 ` if (binCount >= TREEIFY_THRESHOLD) treeifyBin(tab, i);` //20.返回原来key对应的旧值 ` if (oldVal != null) return oldVal; break; } } }` //20.进行扩容判断 ` addCount(1L, binCount); return null; }` get方法: `public V get(Object key) { Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;` //根据这个key算hashcode 与1.7相比没有异或四次 效率更高了 `int h = spread(key.hashCode());` //整个 hashtable对象 table中 tabAt 根据这个key算出来的hash值找对应的value 不为空,看找到的e 返回e的值 。 //由于tab这个节点的val变量是被 volatile修饰 重新赋值(单纯赋值不含任何计算)会被其他线程所知晓。所以get方法不用加锁。 ` if ((tab = table) != null && (n = tab.length) > 0 && (e = tabAt(tab, (n - 1) & h)) != null) { if ((eh = e.hash) == h) { if ((ek = e.key) == key || (ek != null && key.equals(ek))) return e.val; } else if (eh < 0) return (p = e.find(h, key)) != null ? p.val : null; while ((e = e.next) != null) { if (e.hash == h && ((ek = e.key) == key || (ek != null && key.equals(ek)))) return e.val; } } return null; }`
本文来自博客园,作者:迷茫的小白,转载请注明原文链接:https://www.cnblogs.com/cloudHui/p/11728536.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构