Java集合学习:HashMap的原理

一、HashMap里的Hash是什么?

首先,我们先要搞清楚HashMap里的的Hash是啥意思。

当我们在编程过程中,往往需要对线性表进行查找操作。

在顺序表中查找时,需要从表头开始,依次遍历比较a[i]与key的值是否相等,直到相等才返回索引i;在有序表中查找时,我们经常使用的是二分查找,通过比较key与a[i]的大小来折半查找,直到相等时才返回索引i。最终通过索引找到我们要找的元素。

但是,这两种方法的效率都依赖于查找中比较的次数

那能不能不经过比较,而是直接通过关键字key一次得到所要的结果呢?这时,就有了散列表查找(哈希表)

散列技术是指在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使每一个关键字都对应一个存储位置。这样,在查找的过程中,只需要通过这个对应关系f 找到给定值key的映射f(key)。只要集合中存在关键字和key相等的记录,则必在存储位置f(key)处。我们把这种对应关系f 称为散列函数哈希函数。按照这个思想,采用散列技术将记录存储在一块连续的存储空间中,这块连续的存储空间称为哈希表。所得的存储地址称为哈希地址散列地址

相信看到这里大家都懂这个Hash是什么意思了,其实就是散列技术,通过一个对应关系快速找到目标值的位置。

二、HashMap是什么?

HashMap是正是基于哈希表的数据结构,用于存储键值对(key-value)。

HashMap基于键的HashCode值唯一标识一条数据,同时基于键的HashCode值进行数据的存取,因此可以快速更新查询数据,但其每次遍历的顺序无法保证相同。

HashMap的key和value允许为null

HashMap是非线程安全的,即在同一时刻有多个线程同时写HashMap时将可能导致数据的不一致。

如果需要满足线程安全的条件,则可以用Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap

三、HashMap的底层原理

HashMap的核心原理是将键的哈希值映射到数组索引位置,通过数组+链表(在Java 8及之后是数组+链表或红黑树)来处理哈希冲突。

HashMap使用键的hashCode()方法计算哈希值,并通过indexFor方法(JDK1.7之后版本移除了这个方法,直接使用(n-1) & hash)确定元素在数组中的存储位置。哈希值是经过一定扰动处理的,防止哈希值分布不均匀,从而减少哈希冲突。

1、HashMap的数据结构

在这里插入图片描述
HashMap的数据结构如上图所示,其内部是一个数组,数组中的每个元素都是一个单向链表,链表中的每个元素都是嵌套类Entry的实例,Entry实例包含4个属性:key、value、hash值和用于指向单向链表下一个元素的next。

HashMap在查找数据时,根据HashMap的Hash值可以快速定位到数组的具体下标,但是在找到数组下标后需要对链表进行顺序遍历直到找到需要的数据,时间复杂度为O(n)为了减少链表遍历的开销,Java 8对HashMap进行了优化,将数据结构修改为数组+链表或红黑树。在链表中的元素超过8个以后,HashMap会将链表结构转换为红黑树结构以提高查询效率,红黑树是一种自平衡二叉搜索树,能够将最坏情况下的查询复杂度从O(n)降低到O(log N)。如果树中元素的数量低于6个,红黑树会转换回链表,以减少不必要的树操作开销。

Java 8 HashMap的数据结构如下图所示:
在这里插入图片描述

2、hashCode()和equals()的重要性

HashMap的键必须实现hashCode()和equals()方法。hashCode()用于计算哈希值,以决定键的存储位置,而equals()用于比较两个键是否相同。在put操作时,如果两个键的hashCode()相同,但equals()返回false,则这两个键会被视为不同的键,存储在同一个桶的不同位置。

误用hashCode()和equals()会导致HashMap中的元素无法正常查找或插入

3、默认容量与负载因子的选择

HashMap常用的参数如下:

  • capacity:当前数组的容量,默认为16,可以扩容,扩容后数组的大小为当前的两倍,因此该值始终为2n。
  • loadFactor:负载因子,默认为0.75。
  • threshold:扩容的阈值,其值等于capacity×loadFactor。

默认容量是16,负载因子是0.75,这个组合是在性能和空间之间找到平衡。较高的负载因子会减少空间浪费,但增加了哈希冲突的概率;较低的负载因子会增加空间开销,但减少哈希冲突。

如果已知HashMap的容量需求,建议提前设定合适的初始容量,以减少扩容带来的性能损耗。

4、哈希冲突链表法

当要塞入一个键值对的时候,会根据一个hash算法计算key的hash值,然后通过数组大小n-1 & hash值之后,得到一个数组的下标,然后往那个位置塞入这个键值对

hash算法是可能产生冲突的,且数组的大小是有限的,所以很可能通过不同的key计算得到一样的下标,因此为了解决键值对冲突的问题,采用了链表法:

在JDK1.7及之前链表的插入采用的是头插法,即每当发生哈希冲突时,新的节点总是插入到链表的头部,老节点依次向后移动,形成新的链表结构。

多线程的情况下,头插法可能会导致链表形成环,特别是在并发扩容时。

在JDK1.8的时候,改成了尾插法,即新节点插入到链表的尾部,保持插入的顺序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/67981.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SOAFEE 技术研讨会:汽车软件定义与自动驾驶技术探讨

在本次技术研讨会上,来自汽车与科技领域的专家们围绕汽车软件定义及自动驾驶技术展开了深入交流与探讨。从 SOAFEE 蓝图计划的创新性理念,到 Autoware 开源项目及 Open AD Kit 在实际应用中的探索,再到 Edge Workload Abstraction and Orches…

FastJson很快,有什么用?

FastJson 在国内的热度还是挺高的,受到了很多开发者的喜欢。不过,我自己倒没有在项目中用过。我记得刚工作那会新做的一个项目有明确规定禁止使用 FastJson。 昨天看到一篇关于 FastJson 的文章,这位朋友分享了自己在使用 FastJson 遇到的一…

Jetson nano 安装 PCL 指南

本指南帮助 ARM64 架构的 Jetson Nano 安装 PCL(点云库)。 安装步骤 第一步:安装依赖 在终端中运行以下命令,安装 PCL 所需的依赖: sudo apt-get update sudo apt-get install git build-essential linux-libc-dev s…

ansible自动化运维实战--软件包管理模块、服务模块、文件模块和收集模块setup(4)

文章目录 一、软件包管理模块1.1、功能1.2、常用参数1.3、示例 二、服务模块2.1、功能2.2、服务模块常用参数2.3、示例 三、文件与目录模块3.1、file功能3.2、常用参数3.3、示例 四、收集模块-setup4.1、setup功能4.2、示例 一、软件包管理模块 1.1、功能 Ansible 提供了多种…

终极的复杂,是简单

软件仿真拥有最佳的信号可见性和调试灵活性,能够高效捕获很多显而易见的常见错误,被大多数工程师熟练使用。 空间领域应用的一套数据处理系统(Data Handling System),采用抗辐FPGA作为主处理器,片上资源只包含10752个寄存器,软仿也是个挺花时间的事。 Few ms might take …

开关电源设计(2)–BUCK电路设计和计算过程

BUCK电路也即降压电路,是应用最广泛的DC-DC变换器 需求决定的参数 V i 和 V o V_{i} 和V_{o} Vi​和Vo​ f f f I o I_{o} Io​ 输出电压纹波率 效率 需要计算:电感量 r r r 占空比 C 参数计算: 当MOS管导通时,电感电压 V O N V i − …

cudatex文本编辑器

一、下载 通过网盘分享的文件:cudatext 链接: https://pan.baidu.com/s/1TZbGk3AM09SuKvvcQy0d6Q?pwdbbzd 提取码: bbzd 本链接分享的为2025年1月下载上传的软件,版本为1.220.6.1 ;软件皆已放入中文包。 二、设置中文简体 打开 https://…

C语言文件操作:标准库与系统调用实践

目录 1、C语言标准库文件操作 1.1.题目要求: 1.2.函数讲解: fopen 函数原型 参数 常用的打开模式 返回值 fwrite函数 函数原型 参数 返回值 注意事项 fseek函数 函数原型 参数 返回值 fread函数 函数原型 参数 返回值 fclose 函数…

一款专业通用开源的MES生产执行管理系统

系统简介 MES系统是一款B/S结构、开源、免费的生产执行管理系统。 此系统基于本人多年离散智造行业的业务经验及J2EE项目经验开发。 主要目的是为国内离散制造业的中小企业提供一个专业化、通用性、低成本的MES系统解决方案。 系统将提供“售前”、“实施”、“用户培训”、…

SpringBoot集成Flink-CDC,实现对数据库数据的监听

一、什么是 CDC ? CDC 是Change Data Capture(变更数据获取)的简称。 核心思想是,监测并捕获数据库的变动(包括数据或数据表的插入、 更新以及删除等),将这些变更按发生的顺序完整记录下来&…

Three城市引擎地图插件Geo-3d

一、简介 基于Three开发,为Three 3D场景提供GIS能力和城市底座渲染能力。支持Web墨卡托、WGS84、GCJ02等坐标系,支持坐标转换,支持影像、地形、geojson建筑、道路,植被等渲染。支持自定义主题。 二、效果 三、代码 //插件初始化…

应用层协议 HTTP 讲解实战:从0实现HTTP 服务器

🌈 个人主页:Zfox_ 🔥 系列专栏:Linux 目录 一:🔥 HTTP 协议 🦋 认识 URL🦋 urlencode 和 urldecode 二:🔥 HTTP 协议请求与响应格式 🦋 HTTP 请求…

鸿蒙仓颉环境配置(仓颉SDK下载,仓颉VsCode开发环境配置,仓颉DevEco开发环境配置)

目录 ​1)仓颉的SDK下载 1--进入仓颉的官网 2--点击图片中的下载按钮 3--在新跳转的页面点击即刻下载 4--下载 5--找到你们自己下载好的地方 6--解压软件 2)仓颉编程环境配置 1--找到自己的根目录 2--进入命令行窗口 3--输入 envsetup.bat 4--验证是否安…

CPU 缓存基础知识

并发编程首先需要简单了解下现代CPU相关知识。通过一些简单的图,简单的代码,来认识CPU以及一些常见的问题。 目录 CPU存储与缓存的引入常见的三级缓存结构缓存一致性协议MESI协议缓存行 cache line 通过代码实例认识缓存行的重要性 CPU指令的乱序执行通过…

计算机网络 (56)交互式音频/视频

一、定义与特点 定义:交互式音频/视频是指用户使用互联网和其他人进行实时交互式通信的技术,包括语音、视频图像等多媒体实时通信。 特点: 实时性:音频和视频数据是实时传输和播放的,用户之间可以进行即时的交流。交互…

【Linux系统】Linux下的图形库 ncurses(简单认识)

基本介绍 在 Linux 环境下,ncurses 是一个非常重要的库,用于编写可以在终端(TTY)或模拟终端窗口中运行的 字符界面程序。它提供了一套函数,使得开发者可以轻松地操作文本终端的显示,比如移动光标、创建窗口…

基于C#实现多线程启动停止暂停继续

大部分初学者在学习C#上位机编程时,多线程是一个很难逾越的鸿沟,不合理地使用多线程,会导致经常出现各种奇怪的问题,这也是很多初学者不敢使用多线程的原因。但是在实际开发中,多线程是一个不可避免的技术栈&#xff0…

ESP8266 MQTT服务器+阿里云

MQTT私有平台搭建(EMQX 阿里云) 阿里云服务器 EMQX 搭建私有MQTT平台 1、搜索EMQX开源版本 2、查看各版本EMQX支持的UBUNTU版本 3、查看服务器Ubuntu版本 4、使用APT安装模式 5、按照官网指示安装并启动 6、下载安装MQTTX测试工具 7、设置云服务…

Redis使用基础

1 redis介绍 Redis(Remote Dictionary Server ),即远程字典服务 ! 是完全开源的,遵守 BSD 协议,是一个高性能的 key-value 数据库。 使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并…

PostgreSQL主从复制配置

本文主要介绍基于pg_basebackup实现主从复制(异步流复制) MASTER节点安装的方法可以看这篇文章 PostgreSQL YUM安装_yum install postgresql-CSDN博客 关于基本的配置就不作过多的介绍了,直接开始 MASTER节点 首先在master节点创建一个用于…