当前位置: 首页 > 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.xdnf.cn/news/1093249.html

相关文章:

  • Redis-哨兵机制doctor环境搭建
  • 零基础上手 Amazon DynamoDB:NoSQL 数据库服务的核心概念与快速搭建指南
  • 3.常⽤控件
  • 主流大模型Agent框架ChatDev详解
  • 2023年华为杯研究生数学建模竞赛A题WLAN组网分析
  • RAGFlow 与 QAnything 智能切片对比:深度解析与优劣考量
  • 存储服务一NFS文件存储概述
  • python+vue的会议室预定管理系统
  • 板凳-------Mysql cookbook学习 (十一--------6)
  • 池化思想-Mysql异步连接池
  • linux操作命令笔记
  • 【工具变量】上市公司企业金融强监管数据、资管新规数据(2001-2024年)
  • zabbix安装agent并连接
  • 《【第五篇】图片处理自动化:让你的视觉内容更专业!:图片处理基础与批量裁剪》
  • AI 辅导究竟蕴含着怎样的独特优势?​
  • Senior 工程师的定义:深度专精 vs 高层次视野
  • 基于SD-WAN的管件制造数字化产线系统集成方案
  • 【25软考网工】第十章 (3)网络冗余设计、广域网接入技术
  • 项目进度报告缺乏重点,如何提炼关键指标
  • SpringBoot实现MCP
  • Java SE--继承
  • 基于Java+Maven+Testng+Selenium+Log4j+Allure+Jenkins搭建一个WebUI自动化框架(4)集成Allure报表
  • 机器视觉之工业相机讲解
  • 鸿蒙商城开发:ZKmall开源商城系统特性适配与性能优化
  • 【PyTorch】PyTorch中torch.nn模块的全连接层
  • vscode 防止linux索引爆红
  • Java+AI精准广告革命:实时推送系统实战指南
  • JVM 调优
  • 打破传统,开启 AR 智慧课堂​
  • 矩阵之方阵与行列式的关系