当前位置: 首页 > news >正文

HashMap的get、put流程源码分析

get 方法源码如下


final Node<K, V> getNode(int hash, Object key) {Node<K, V>[] tab;    // 哈希桶数组引用Node<K, V> first, e; // first: 桶内首节点; e: 遍历用临时节点int n; K k;          // n: 数组长度; k: 临时存储节点的键// 1. 检查哈希表状态:数组非空且长度>0,且目标桶存在节点if ((tab = table) != null && (n = tab.length) > 0 &&(first = tab[(n - 1) & hash]) != null) {// 2. 优先检查首节点:哈希值匹配 + 键匹配(引用或 equals)if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k)))) {return first;}// 3. 存在后续节点时,区分链表/红黑树处理if ((e = first.next) != null) {// 3.1 红黑树节点:调用树查找方法if (first instanceof TreeNode) {return ((TreeNode<K, V>) first).getTreeNode(hash, key);}// 3.2 链表节点:遍历查找do {if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) {return e;}} while ((e = e.next) != null); // 遍历到链表末尾}}// 4. 未找到节点return null;}

get方法通过getNode实现键值对查找,核心是利用哈希定位与数据结构适配实现高效查询,步骤如下:
1.哈希值计算
调用hash(key)生成二次哈希值,确保与插入时的定位逻辑一致。
2.索引定位与节点查找
计算索引后,检查哈希表状态及目标位置:
哈希表为空、长度为 0 或索引位置无节点:返回null;
索引位置首节点键匹配:直接返回该节点;
首节点不匹配时,根据结构处理:
红黑树节点:调用getTreeNode从根节点开始按红黑树规则查找;
链表节点:遍历链表逐一比对,找到匹配节点则返回,否则返回null。
3.效率优化
优先判断首节点(最常访问),避免无效遍历;红黑树将查找复杂度从链表的 O (n) 降至 O (log n),平衡查询效率。

put 方法源码如下

// 核心插入方法final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {Node<K, V>[] tab; Node<K, V> p; int n, i;// 1. 哈希表未初始化或为空时,进行扩容(初始化)if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;// 2. 计算索引,定位哈希桶,桶为空则直接插入新节点if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {Node<K, V> e; K k;// 3. 检查首节点是否匹配(哈希和键都匹配)if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;// 4. 首节点是红黑树节点,调用红黑树插入逻辑else if (p instanceof TreeNode)e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value);// 5. 首节点是链表节点,遍历链表处理else {for (int binCount = 0; ; ++binCount) {// 遍历到链表尾部,插入新节点if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);// 链表长度达标,尝试转红黑树if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash);break;}// 找到已存在的节点,跳出遍历if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}// 6. 处理已存在的节点,更新值if (e != null) { V oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e); return oldValue;}}// 7. 插入新节点后,检查是否需要扩容、更新计数等++modCount;if (++size > threshold)resize();afterNodeInsertion(evict); return null;}

put方法的核心是通过putVal实现键值对的插入或更新,整体流程围绕哈希计算、节点定位、结构适配及动态扩容展开,具体步骤如下:

1.哈希值计算
调用hash(key)对键进行二次哈希(高 16 位与低 16 位异或),减少哈希冲突,为后续定位存储位置提供依据。
2.哈希表初始化 / 扩容
若哈希表(table)未初始化(为空或长度为 0),通过resize()方法完成初始化(默认容量 16,负载因子 0.75,阈值 12);若后续元素数量超过阈值,resize()会将容量翻倍(确保为 2 的幂次方),维持高效存储。
3.索引定位与节点插入
利用(n-1) & hash计算存储索引(n为当前容量,位运算等价于取模且更高效),根据索引位置状态处理:
位置为空:直接创建新节点插入;
位置非空:
首节点键匹配(哈希值与键均相等):更新节点值;
首节点为红黑树节点:调用putTreeVal按红黑树规则插入;
首节点为链表节点:遍历链表,找到匹配节点则更新值,否则尾部插入新节点;当链表长度达到树化阈值(8)且容量≥64 时,调用treeifyBin转为红黑树(容量不足 64 时优先扩容)。
4.计数与扩容检查
插入新节点后,modCount(结构修改计数器)自增,size(元素总数)加 1;若size超过阈值,触发resize()扩容,保证哈希表性能。

http://www.lryc.cn/news/583265.html

相关文章:

  • sql查询davinci看板数据
  • 《解构this:JavaScript中动态指向的隐秘逻辑》
  • PHP语法高级篇(一):日期时间处理和包含文件
  • 美股异动|机器人概念表现活跃,微美全息(WIMI.US)瞄准高增长赛道涨超14%
  • 2023年IEEE TITS SCI2区TOP,增强回溯搜索算法EBSA+多无人机辅助商业包裹递送系统飞行规划,深度解析+性能实测
  • 第4章:实战项目一 打造你的第一个AI知识库问答机器人 (RAG)
  • LeetCode 138题解 | 随机链表的复制
  • 光伏无人机3D建模:毫秒级精度设计
  • 老年人与机器人玩具的情感连接
  • 什么是 AMR 格式?简鹿音频转换器轻松批量转换 AMR 为 MP3
  • 论文阅读|汽车虚拟环绕音响系统设计与实现策略的比较研究
  • OpenCV图片操作100例:从入门到精通指南(4)
  • NLP:初识RNN模型(概念、分类、作用)
  • 继承与多态:面向对象编程的两大支柱
  • stockapi股票实时tick数据,技术指标macd,kdj,cci,日k线数据
  • 如何将FPGA设计的验证效率提升1000倍以上(3)
  • oracle ocp题库有多少道题,以及题库背诵技巧
  • JavaEE初阶第八期:解锁多线程,从 “单车道” 到 “高速公路” 的编程升级(六)
  • 软件设计师中级概念题
  • Selenium+Pytest自动化测试框架实战前言#
  • 汽车工业制造领域与数字孪生技术的关联性研究​
  • Microsoft AZ-305 Exam Question
  • 迁移Oracle SH 示例 schema 到 PostgreSQL
  • 亚马逊广告进阶指南:长尾词应如何去挖掘
  • RapidRAW RAW 图像编辑器
  • 游戏开发学习记录
  • 码云创建分支
  • 分库分表之实战-sharding-JDBC绑定表配置实战
  • 掌握PDF转CAD技巧,提升工程设计效率
  • 模型内部进行特征提取时,除了“减法”之外,还有哪些技术