跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

在Java中HashMap的put过程是怎样的_Java数据插入流程解析

HashMap.put()执行时先计算扰动哈希值,再通过位运算定位桶索引;若桶为空则直接插入,否则遍历链表或红黑树判断是否覆盖,链表长度≥8且数组长度≥64时树化,扩容阈值为容量×负载因子。 put() 方法执行时发生了什么 Java 中
HashMap.put()
不是简单地把 键值对 塞进数组,而是一套带哈希计算、冲突处理和动态扩容的完整流程。核心逻辑在 JDK 8+ 中已从“链表头插”改为“尾插”,且当链表长度 ≥ 8 且桶数组长度 ≥ 64 时,会转为红黑树。 哈希值计算与索引定位的关键细节
HashMap
对
key.hashCode()
做了二次扰动:
(h = key.hashCode()) ^ (h >>> 16)
,目的是让高位也参与取模运算,降低低位相同导致的哈希碰撞概率。最终桶索引通过
tab[(n - 1) & hash]
计算(
n
是数组长度,必须是 2 的幂),这比取模
% n
更快,但要求容量始终是 2 的整数次幂。 如果
key
为
null
,哈希值固定为 0,总是映射到索引 0 的桶 自定义类作
key
时,必须重写
hashCode()
和
equals()
,否则可能无法正确
get()
哈希值相同不等于
equals()
成立,所以后续仍需调用
equals()
判断是否覆盖 链表转红黑树的触发条件容易被误读 常见误解是“只要链表长度 ≥ 8 就转树”,实际需要同时满足两个条件:
table.length >= 64
且
binCount >= 8
(即遍历链表后计数达到 8)。若数组太小(如初始容量 16),即使某桶链表很长,也不会树化,而是先触发扩容。 扩容阈值默认是
capacity * loadFactor
(初始为 16 × 0.75 = 12) 扩容后数组长度翻倍,所有元素重新哈希再分配,开销较大,应根据预估数据量合理设置初始容量 红黑树节点
TreeNode
比普通
Node
占用更多内存,树化是空间换查询时间的权衡 并发场景下 put() 的危险行为
HashMap
本身不是线程安全的。多线程同时
put()
可能导致死循环(JDK 7 链表头插 + 扩容重哈希时)、 数据丢失 或
get()
返回
null
等未定义行为。这不是 bug,是设计使然。 Eclipse导入Android或其他的JAVA项目的正确方法 WORD版 本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看 下载 立即学习 “ Java免费学习笔记(深入) ”; JDK 8 中死循环问题已修复(改用尾插),但并发
put()
仍可能丢失更新 如需线程安全,优先选
ConcurrentHashMap
;若只是读多写少,可考虑
Collections.synchronizedMap()
禁止在 foreach 或迭代器遍历时调用
put()
,会抛
ConcurrentModificationException
// 示例:手动模拟 put 过程的关键判断点 int hash = hash(key.hashCode()); // 扰动哈希 int i = (table.length - 1) & hash; Node p = table[i]; if (p == null) { table[i] = newNode(hash, key, value, null); // 直接插入 } else if (p.hash == hash && Objects.equals(p.key, key)) { p.value = value; // 覆盖旧值 } else if (p instanceof TreeNode) { e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); // 树中插入 } else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 因为从 0 开始计数 treeifyBin(tab, hash); // 满足条件才尝试树化 break; } if (e.hash == hash && Objects.equals(e.key, key)) break; p = e; } }
真正影响性能的往往不是单次
put()
,而是哈希分布不均、频繁扩容或误用线程不安全场景。这些地方一旦出问题,现象隐蔽,排查成本远高于提前设好初始容量或选对并发容器。

相关文章