深入解析Java HashMap的putVal方法
Java中的HashMap是我们在开发中经常使用的集合之一,它提供了基于哈希表的数据存储方式,使得对数据的插入、删除和查找操作都具有较高的效率。在本文中,我们将深入解析HashMap中的putVal方法,揭示其内部工作原理。通过对代码的逐行分析,我们不仅能够更好地理解HashMap的设计和实现,还能提高我们在实际开发中对HashMap的使用水平。
一、方法概述
putVal方法是HashMap中的核心方法之一,主要用于向HashMap中插入键值对。它的签名如下:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)
参数说明:
hash:键的哈希值。key:键。value:值。onlyIfAbsent:是否仅在键不存在时才插入。evict:是否在插入后进行驱逐操作。
该方法的返回值是插入前与键关联的旧值,如果没有旧值则返回null。
二、代码详细分析
下面我们将对putVal方法的每一部分进行详细的分析。
1. 初始化table
HashMap.Node<K, V>[] tab;
HashMap.Node<K, V> p;
int n, i;
if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;
在这段代码中,首先定义了局部变量tab、p、n和i。接着检查table是否为空或长度为0。如果是,则通过resize()方法进行初始化。这一步确保了HashMap的底层数组table已经被初始化且具有一定的容量。
2. 计算索引并插入新节点
if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);
这里通过 (n - 1) & hash 计算出键值对应该插入的索引位置,并检查该位置是否为空。如果为空,直接在该位置创建一个新的节点。值得注意的是,为什么使用 (n - 1) & hash 而不是 n & hash。因为 (n - 1) 能确保结果在 0 到 n-1 之间,正好是数组的有效索引范围。
3. 处理哈希冲突
else {HashMap.Node<K, V> e;K k;if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof HashMap.TreeNode)e = ((HashMap.TreeNode<K, V>) 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)treeifyBin(tab, hash);break;}if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) {V oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}
}
这部分代码处理哈希冲突的情况。哈希冲突发生时,同一个索引位置可能会有多个节点。为了处理这些节点,HashMap使用了链表和红黑树两种数据结构。
- 覆盖旧值:首先检查当前节点的哈希值和键是否与待插入的键值对相同。如果相同,直接进行覆盖。
- 红黑树节点:如果当前节点是红黑树节点,通过
putTreeVal方法处理。 - 链表节点:如果是链表节点,通过遍历链表找到适当的位置插入新节点。如果链表长度超过阈值(默认是8),会将链表转换为红黑树。
4. 维护结构修改计数和大小
++modCount;
if (++size > threshold)resize();
afterNodeInsertion(evict);
return null;
最后,更新modCount,表示HashMap的结构发生了变化。然后检查当前大小是否超过阈值,如果超过则进行扩容。扩容通过resize方法完成。最后调用afterNodeInsertion方法执行插入后的操作,返回null表示插入成功且没有旧值被覆盖。
三、关键细节与实现原理
1. 哈希函数
在HashMap中,哈希函数的质量直接影响哈希表的性能。HashMap通过对键的哈希码进行二次扰动来减少哈希冲突,提高哈希分布的均匀性。
2. 链表与红黑树
HashMap最初使用链表来处理哈希冲突,但链表在极端情况下会退化为线性查找,性能较差。为了解决这个问题,Java 8引入了红黑树,当链表长度超过阈值时(默认是8),会将链表转换为红黑树,以提高查找效率。
3. 扩容机制
HashMap的扩容机制通过resize方法实现。每次扩容都会将容量扩大为原来的两倍,并重新计算所有元素的索引位置。扩容是一个代价较高的操作,因此HashMap会尽量延迟扩容,直到元素数量超过阈值。
四、优化与最佳实践
1. 初始容量设置
为了减少扩容次数,可以在创建HashMap时设置一个合理的初始容量。这样在插入大量元素时,可以减少扩容操作,提高性能。
2. 合理选择负载因子
HashMap的负载因子(默认是0.75)决定了扩容的阈值。负载因子越大,空间利用率越高,但哈希冲突的概率也越大。根据具体情况,可以选择合适的负载因子,以平衡空间利用率和性能。
3. 避免使用可变对象作为键
如果使用可变对象作为键,在对象状态变化后,哈希值可能会改变,导致无法正确查找到对应的值。因此,尽量使用不可变对象(如String、Integer等)作为键。
五、总结
通过对HashMap中putVal方法的深入分析,我们了解了HashMap处理插入操作的详细过程,包括初始化、哈希冲突处理、扩容机制等。理解这些内部细节,不仅有助于我们更好地使用HashMap,还能在需要时对其进行优化,提升程序的性能。
在实际开发中,选择合适的数据结构和算法是至关重要的。HashMap作为Java中常用的集合类,其高效的实现和灵活的使用方式,使得它在众多应用场景中得到了广泛的应用。希望通过本文的分析,能够帮助读者更深入地理解HashMap的内部机制,提高编程技巧和代码质量。
相关文章:
深入解析Java HashMap的putVal方法
Java中的HashMap是我们在开发中经常使用的集合之一,它提供了基于哈希表的数据存储方式,使得对数据的插入、删除和查找操作都具有较高的效率。在本文中,我们将深入解析HashMap中的putVal方法,揭示其内部工作原理。通过对代码的逐行…...
使用智谱 GLM-4-9B 和 SiliconCloud 云服务快速构建一个编码类智能体应用
本篇文章我将介绍使用智谱 AI 最新开源的 GLM-4-9B 模型和 GenAI 云服务 SiliconCloud 快速构建一个 RAG 应用,首先我会详细介绍下 GLM-4-9B 模型的能力情况和开源限制,以及 SiliconCloud 的使用介绍,最后构建一个编码类智能体应用作为测试。…...
关于vue2 antd 碰到的问题总结下
1.关于vue2 antd 视图更新问题 1.一种强制更新 Vue2是通过用Object…defineProperty来设置数据的getter和setter实现对数据和以及视图改变的监听的。对于数组和对象这种引用类型来说,getter和setter无法检测到它们内部的变化。用这种 this.$set(this.form, "…...
常见的api:Runtime Object
一.Runtiem的成员方法 1.getRuntime() 当前系统的运行环境 2.exit 停止虚拟机 3.avaliableProcessors 获取Cpu线程的参数 4.maxMemory JVM能从系统中获取总内存大小(单位byte) 5.totalMemory JVM已经从系统中获取总内大小(单位byte) 6.freeMemory JVM剩余内存大小(…...
Linux守护进程揭秘-无声无息运行在后台
在Linux系统中,有一些特殊的进程悄无声息地运行在后台,如同坚实的基石支撑着整个系统的运转。它们就是众所周知的守护进程(Daemon)。本文将为你揭开守护进程的神秘面纱,探讨它们的本质特征、创建过程,以及如何重定向它们的输入输出…...
python-Bert(谷歌非官方产品)模型基础笔记0.1.096
python-bert模型基础笔记0.1.015 TODOLIST官网中的微调样例代码Bert模型的微调限制Bert的适合的场景Bert多语言和中文模型Bert模型两大类官方建议模型Bert模型中名字的含义Bert模型包含的文件Bert系列模型参数介绍微调与迁移学习区别Bert微调的方式Pre-training和Fine-tuning区…...
Linux的命令补全脚本
一 linux命令补全脚本 Linux的命令补全脚本是一个强大且高效的工具,它能够极大地提高用户在命令行界面的工作效率。这种脚本通过自动完成部分输入的命令或参数,帮助用户减少敲击键盘的次数并降低出错率。接下来将深入探讨其工作原理、安装方式以及如何自…...
前端 JS 经典:打印对象的 bug
1. 问题 相信这个 console 打印语句的 bug,其实小伙伴们是遇到过的,就是你有一个对象,通过 console,打印一次,然后经过一些处理,再通过 console 打印,发现两次打印的结果是一样的,第…...
大型语言模型简介
大型语言模型简介 大型语言模型 (LLM) 是一种深度学习算法,可以使用非常大的数据集识别、总结、翻译、预测和生成内容。 文章目录 大型语言模型简介什么是大型语言模型?为什么大型语言模型很重要?什么是大型语言模型示例?大型语…...
javaWeb4 Maven
Maven-管理和构建java项目的工具 基于POM的概念 1.依赖管理:管理项目依赖的jar包 ,避免版本冲突 2.统一项目结构:比如统一eclipse IDEA等开发工具 3.项目构建:标准跨平台的自动化项目构建方式。有标准构建流程,能快速…...
eclipse连接后端mysql数据库并且查询
教学视频:https://www.bilibili.com/video/BV1mK4y157kE/?spm_id_from333.337.search-card.all.click&vd_source26e80390f500a7ceea611e29c7bcea38本人eclipse和up主不同的地方如下,右键项目名称->build path->configure build path->Libr…...
Windows mstsc
windows mstsc 局域网远程计算机192.168.0.113为例,远程控制命令mstsc...
百度/迅雷/夸克,网盘免费加速,已破!
哈喽,各位小伙伴们好,我是给大家带来各类黑科技与前沿资讯的小武。 之前给大家安利了百度网盘及迅雷的加速方法,详细方法及获取参考之前文章: 刚刚!度盘、某雷已破!速度50M/s! 本次主要介绍夸…...
SOA的参考架构
1. 以服务为中心的企业集成架构 IBM的Websphere业务集成参考架构(如图1所示,以下称参考架构)是典型的以服务为中心的企业集成架构。 图1 IBM WebSphere业务集成参考架构 以服务为中心的企业集成采用“关注点分离(Separation of Co…...
前端开发-表单和表格的区别
在前端开发中,表单(Form)和表格(Table)同样具有不同的用途和结构: 前端表单(Form): 数据收集:表单用于收集用户输入的数据,如文本输入、选择选项等。用户交…...
Data Management Controls
Data Browsing and Analysis Data Grid 以标准表格或其他视图格式(例如,带状网格、卡片、瓷砖)显示数据。Vertical Grid 以表格形式显示数据,数据字段显示为行,记录显示为列。Pivot Grid 模拟微软Excel的枢轴表功…...
NextJs 数据篇 - 数据获取 | 缓存 | Server Actions
NextJs 数据篇 - 数据获取 | 缓存 | Server Actions 前言一. 数据获取 fetch1.1 缓存 caching① 服务端组件使用fetch② 路由处理器 GET 请求使用fetch 1.2 重新验证 revalidating① 基于时间的重新验证② 按需重新验证revalidatePathrevalidateTag 1.3 缓存的退出方式 二. Ser…...
腾讯开源人像照片生成视频模型V-Express
网址 https://github.com/tencent-ailab/V-Express 下面是github里的翻译: 在人像视频生成领域,使用单张图像生成人像视频变得越来越普遍。一种常见的方法是利用生成模型来增强受控发电的适配器。 但是,控制信号的强度可能会有所不同&…...
pytorch使用DataParallel并行化保存和加载模型(单卡、多卡各种情况讲解)
话不多说,直接进入正题。 !!!不过要注意一点,本文保存模型采用的都是只保存模型参数的情况,而不是保存整个模型的情况。一定要看清楚再用啊! 1 单卡训练,单卡加载 #保存模型 torc…...
PS初级|写在纸上的字怎么抠成透明背景?
前言 上一次咱们讲了很多很多很多的抠图教程,这次继续。。。最近有小伙伴问我:如果是写在纸上的字,要怎么把它抠成透明背景。 这个其实很简单,直接来说就是选择通道来抠。但有一点要注意的是,写在纸上的字࿰…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...
Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...
sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...
Canal环境搭建并实现和ES数据同步
作者:田超凡 日期:2025年6月7日 Canal安装,启动端口11111、8082: 安装canal-deployer服务端: https://github.com/alibaba/canal/releases/1.1.7/canal.deployer-1.1.7.tar.gz cd /opt/homebrew/etc mkdir canal…...
