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;
}`
posted @   迷茫的小白  阅读(818)  评论(0编辑  收藏  举报
编辑推荐:
· 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语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示