【深入挖掘Java技术】「源码原理体系」盲点问题解析之HashMap工作原理全揭秘(上)

HashMap工作原理全揭秘 — 核心源码解析

  • 知识盲点
  • 概念介绍
  • 数据结构
    • 数组
    • 链表
    • 数组VS链表
    • 哈希表
    • 不同JVM版本HashMap的展现形式
  • HashMap VS HashTable
    • 特性区别对比
  • hashcode
    • hashCode的作用
    • equals方法和hashcode的关系
    • key为null怎么办
    • 执行步骤
  • 核心参数
    • 容量探讨
    • 负载因子探讨
      • 加载因子过高
        • 加载因子与空间开销
        • 查询成本与加载因子
        • 减少扩容次数和成本
          • 设置初始容量与加载因子
          • 总结

知识盲点

在这里插入图片描述

概念介绍

HashMap是基于Map接口构建的数据结构,它以键值对的形式存储元素,允许键和值都为null。由于键的唯一性,HashMap中只能有一个键为null。HashMap的特点是元素的无序性和不重复性。

注意,HashMap并不是线程安全的。在多线程环境下,如果不进行适当的同步处理,可能会导致数据不一致或其他并发问题。因此,对于需要高并发访问的场景,建议使用线程安全的替代方案,如ConcurrentHashMap

数据结构

在HashMap的数据结构中,数组和链表是核心组件,但它们在实现上有着根本性的差异。

  • 数组是静态的,一旦创建,其大小就无法改变
    • 数组由于其固定的大小,对于大量数据的处理可能会遇到性能瓶颈。
  • 链表是动态的,可以根据需要随时添加或删除节点。
    • 链表则可以灵活地扩展,更好地应对数据增长的需求,链表在内存使用上可能更加碎片化,因为需要为新节点分配空间并在不再需要时进行回收。

数组

数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);数组的特点是:寻址容易,插入和删除困难;

链表

链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特点是:寻址困难,插入和删除容易。

数组VS链表

  • 数组的特点:查询效率高,插入和删除效率低
  • 链表的特点:查询效率低,插入和删除效率高

哈希表

综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?这就是我们要提起的哈希表。哈希表既满足了数据的查找方便,同时不占用太多的内容空间,使用也十分方便。

非同步和允许使用null之外,HashMap类与Hashtable大致相同。此类不保证映射的顺序,特别是它不保证该顺序恒久不变发生扩容时,元素位置会重新分配

不同JVM版本HashMap的展现形式

JDK8之后的版本,HashMap底层使用数组加(链表或红黑树)的结构完美的解决了数组和链表的问题(循环死锁问题),使的查询和插入,删除的效率都很高。
在这里插入图片描述
HashMap的散列表是懒加载机制,在第一次put的时候才会创建hash表

HashMap VS HashTable

在多线程环境中,HashMap由于其非线程安全的特性,性能可能更高。相比之下,Hashtable通过在实现方法中添加synchronized关键字确保线程安全,因此在性能上可能稍逊一筹。

如果没有特殊需求,建议在常规使用中选择HashMap,多线程环境下,如果需要线程安全的集合,可以使用Collections.synchronizedMap()方法将HashMap转换为线程安全的集合

特性区别对比

在这里插入图片描述

  • 是否允许键为空:值得一提的是,HashMap允许键为null,而Hashtable的键则不可为null。

  • 继承结构的不同 :HashMap是对Map接口的直接实现,而Hashtable不仅实现了Map接口,还继承了Dictionary抽象类。

    • 在这里插入图片描述
    • 在这里插入图片描述
  • 扩充数据量不同 :关于初始容量和扩容策略,HashMap的初始容量为16,而Hashtable的初始容量为11。两者的填充因子默认都是0.75。当需要扩容时,HashMap的容量会翻倍,即capacity * 2; 而Hashtable的容量会在原有基础上增加1,即capacity * 2 + 1。

  • 数据安全的问题 :在单线程环境下或对性能要求较高的场景中,HashMap可能是一个更好的选择。而在多线程环境中,如果需要确保线程安全,则应考虑使用Hashtable或通过Collections.synchronizedMap()方法将HashMap转换为线程安全的集合。

hashcode

在HashMap中,当我们要存储一个键值对时,首先会调用对象的hashCode()方法来获取哈希码。这个哈希码的主要目的是为了确定对象在哈希表中的位置。

为了得到一个更均匀的分布,提高查找效率,hashCode()返回的整数会经过一系列的位操作(如右移和异或)来进一步处理。这些操作的主要目的是为了打乱哈希码的高位和低位,使得不同的键产生的哈希码有更好的随机性,从而减少冲突的可能性。

hashCode的作用

hashCode的存在主要是为了查找的快捷性, hashCode是用来在散列存储结构中确定对象的存储地址的 (用hashcode来代表对象在hash表中的位置) 。

hashCode存在的重要的原因之一就是在HashMap(HashSet其实就是HashMap)中使用(其实Object类的hashCode方法注释已经说明了)。

HashMap之所以速度快,因为他使用的是散列表,根据key的hashcode值生成数组下标(通过内存地址直接查找,不需要判断,但是需要多出很多内存,相当于以空间换时间)

equals方法和hashcode的关系

若重写了equals(Object obj)方法,则有必要重写hashCode()方法
在这里插入图片描述

  • 若两个对象equals(Object obj)返回true,则hashCode()有必要也返回相同的int数
  • 若两个对象equals(Object obj)返回false,则hashCode()不一定返回不同的int数
  • 若两个对象hashCode()返回相同int数,则equals(Object obj)不一定返回true
  • 若两个对象hashCode()返回不同int数,则equals(Object obj)一定返回false

同一对象在执行期间若已经存储在集合中,则不能修改影响hashCode值的相关信息,否则会导致内存泄露问题。

key为null怎么办

key为null的时候,只会放在hashMap的0位置(即key的hashCode为0,对数组长度取余后的下标也是0),不会有链表在HashMap源码中对put方法对null做了处理。

  1. key为null的判断后进入putForNullKey(V value)这个方法,里面for循环是在table[0]链表中查找key为null的元素。

  2. 如果找到,则将value重新赋值给这个元素的value,并返回原来的value。如果没找到则将这个元素添加到table[0]链表的表头。

执行步骤

  • 计算原始哈希码:调用对象的hashCode()方法来获取一个原始的哈希码。
  • 计算哈希表索引:对原始哈希码进行位操作(如右移和异或),与Bucket大小进行取模,得到一个最终的哈希表索引。这个索引用于确定对象在哈希表中的位置。

核心参数

HashMap的实例有两个参数影响其性能:初始容量和加载因子。

  • 容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。
  • 加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度

迭代collection视图所需的时间与HashMap实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。

容量探讨

HashMap的最小树形化容量,这个值的意义是:位桶(bin)处的数据要采用红黑树结构进行存储时,整个Table的最小容量(存储方式由链表转成红黑树的容量的最小阈值)当哈希表中的容量大于这个值时,表中的桶才能进行树形化,否则桶内元素太多时会扩容,而不是树形化为了避免进行扩容、树形化选择的冲突,这个值不能小于4 * TREEIFY_THRESHOLD(16)

如果很多映射关系要存储在HashMap实例中,则相对于按需执行自动的rehash操作以增大表的容量来说,使用足够大的初始容量创建它将使得映射关系能更有效地存储。

负载因子探讨

加载因子是用于控制哈希表中元素数量与内部数组大小之间关系的参数。

加载因子过高

加载因子越高,哈希表中的元素数量可以更多,但同时可能导致更多的冲突,从而增加查询成本。

加载因子与空间开销

当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行rehash操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数,通常,默认加载因子(0.75)在时间和空间成本上寻求一种折衷。

当加载因子设置得较高时,哈希表中的元素数量可以更多,从而减少了当内部数组需要扩容时所浪费的空间。这似乎是节省了空间,但实际上,这也意味着更高的冲突可能性。

查询成本与加载因子

当哈希表中的元素数量增加时,发生冲突的可能性也增加。这意味着查找特定键的时间会增加,因为可能需要遍历更长的链表(或红黑树,如果链表长度过长)。因此,高的加载因子会增加查询成本。

减少扩容次数和成本
设置初始容量与加载因子

在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少rehash操作次数,如果初始容量大于最大条目数除以加载因子,则不会发生rehash操作。

  • 减少扩容的次数:如果你预计哈希表将包含大量元素,那么选择一个较大的初始容量可能是一个好主意。

  • 较大的初始容量:如果初始容量大于(最大条目数除以加载因子),那么不会发生rehash操作。这意味着,为了减少rehash次数,你可能需要选择一个较大的初始容量。

总结

加载因子是一个权衡参数。高的加载因子可以减少空间浪费,但可能会增加查询成本和rehash操作的次数。

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

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

相关文章

第21集《佛法修学概要》

乙三、修行篇第三分三:丙一、总标五乘要义;丙二、别明五乘解脱法门;丙三、结归一佛乘 请大家打开讲义第五十八页。我们讲到乙三,修行篇第三。 大乘佛法有八万四千个法门,但是我们可以把它归纳成两个重点:…

3-微信小程序组件基本用法

小程序组件是由宿主环境提供的,开发者可以基于组件快速搭建出页面结构。官方把小程序组件分为9类。 视图容器基础内容表单组件导航组件媒体组件map地图组件canvas画布组件开放能力无障碍访问 常用视图组件 view 官网传送门 普通视图区域 类似于HTML中div&#x…

Python基础语法(中)—— python列表、字符串、函数

文章目录 5. python中的列表5.1 列表的初始化5.1.1 直接初始化5.1.2 通过append函数初始化5.1.3 通过for语句初始化列表长度和每个位置的数值 5.2访问列表元素5.3使用循环语句遍历列表5.4列表的切片操作5.5列表的复制5.6列表的运算5.7列表的常用操作5.8嵌套列表5.9列表其他小知…

基于springboot+vue的网上花卉商城系统(Java毕业设计)

大家好,我是DeBug,很高兴你能来阅读!作为一名热爱编程的程序员,我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里,我将会结合实际项目经验,分享编程技巧、最佳实践以及解决问题的方法。无论你是…

「 网络安全术语解读 」点击劫持Clickjacking详解

引言:要想深入理解点击劫持攻击,我们需要先清楚iframe的用途及优缺点。 1. 关于iframe iframe是HTML语言中的一部分,通常用于在网页中嵌入其他网页的内容,如图像、视频、音频、链接等。它允许在一个网页中插入另一个网页&#xf…

高性能mysql 第三版 读书笔记

MySQL中的tmp_table_size和max_heap_table_size|极客笔记 mysql占用内存过高调优方法_tmp_table_size过大阻塞-CSDN博客 查看mysql分配的内存 mysql查看内存利用状态_mob6454cc6d81c9的技术博客_51CTO博客 https://www.cnblogs.com/stronger-xsw/p/13632505.html

国企石油化工单位任职资格体系搭建案例

传统的任职资格体系主要考虑年限、经验、资历等因素,部分企业在任职资格体系中也引入了能力指标,但是,实际管理过程中仍然存在很多问题:员工“熬年头”意识严重、工作积极性差、优秀人员因得不到晋升而流失、各方面能力都不错的人…

【一文详解】知识分享:(C#开发学习快速入门)

面向对象(OOP) c语言是面向过程。 c是面向过程面向对象。 c#是纯粹的面向对象: 核心思想是以人的思维习惯来分析和解决问题。万物皆对象。 面向对象开发步骤: 分析对象 特征行为关系(对象关系/类关系) 写代码: 特征–>成员变量 方法–>成员方法 实例化–具体对象 …

Python商业数据挖掘实战——爬取网页并将其转为Markdown

前言 「作者主页」:雪碧有白泡泡 「个人网站」:雪碧的个人网站 ChatGPT体验地址 文章目录 前言前言正则表达式进行转换送书活动 前言 在信息爆炸的时代,互联网上的海量文字信息如同无尽的沙滩。然而,其中真正有价值的信息往往埋…

ORA-12541:TNS:无监听程序

1.重新配置监听 找到监听程序配置,右键已管理员身份运行 选择第二个:重新配置 这个一般没什么好选的 默认选定的协议TCP,继续下一步 默认的否 继续下一步,完成监听重新配置 之后进行测试看能否连上 2.本地Net服务名配置 …

Arrow:在项目中进行时间处理的强大工具

目录 一、Arrow简介 二、安装与配置 三、基础功能与使用 1. 日期和时间格式转换 2. 时区处理 3. 时间序列分析 四、进阶应用与案例分析 五、性能与优化 六、最佳实践与经验分享 七、总结与展望 在处理日期和时间时,我们经常需要一个精确、可靠的库来帮助我…

【漏洞复现】锐捷EG易网关cli.php后台命令执行漏洞

Nx01 产品简介 锐捷EG易网关是一款综合网关,由锐捷网络完全自主研发。它集成了先进的软硬件体系架构,配备了DPI深入分析引擎、行为分析/管理引擎,可以在保证网络出口高效转发的条件下,提供专业的流控功能、出色的URL过滤以及本地化…

MySQL批量插入技巧

关于MySQL批量插入的一些问题 MySQL一直是我们互联网行业比较常用的数据,当我们使用半ORM框架进行MySQL大批量插入操作时,你是否考虑过这些问题: 进行大数据量插入时,是否需要进行分批次插入,一次插入多少合适?有什么…

Elasticsearch:Search tutorial - 使用 Python 进行搜索 (三)

这个是继上一篇文章 “Elasticsearch:Serarch tutorial - 使用 Python 进行搜索 (二)” 的续篇。在今天的文章中,本节将向你介绍一种不同的搜索方式,利用机器学习 (ML) 技术来解释含义和上下文。 向量搜索 嵌入 (embed…

【Python机器学习】深度学习——调参

先用MLPClassifier应用到two_moons数据集上: from sklearn.neural_network import MLPClassifier from sklearn.datasets import make_moons from sklearn.model_selection import train_test_split import mglearn import matplotlib.pyplot as pltplt.rcParams[f…

训练营第四十二天 | 01背包问题,你该了解这些! ● 01背包问题,你该了解这些! 滚动数组 ● 416. 分割等和子集

01背包问题 二维 代码随想录 dp二维数组 优化 01背包问题 一维 代码随想录 dp一维数组 416. 分割等和子集 把数组分成总和相等的两份,如果数组总和为奇数,不能分割,若有符合的数组子集,返回true 代码随想录 class Solution {p…

数据中心建设之——理解基于财务三大报表的BI指标体系搭建

目录 1.1 三张报表的作用 1.2 三张报表长的样子 1.2.1 资产负债表 1.2.2 利润表 1.2.3 现金流 1.3 BI指标构建 1.3.1 盈利能力指标构建 1.3.2 营运能力指标构建 1.3.3 偿债能力指标构建 转眼间,一年又悄然而逝,时光荏苒,岁月如梭 &a…

仓储|仓库管理水墨屏RFID电子标签2.4G基站CK-RTLS0501G功能说明与安装方式

随着全球智能制造进度的推进以及物流智能化管理水平的升级,行业亟需一种既能实现RFID批量读取、又能替代纸质标签在循环作业、供应链管理以及实现动态条码标签显示的产品。在此种行业需求背景下,我是适时推出了基于墨水屏显示技术的VT系列可视化超高频标…

JVM-JVM支持高并发底层原理精讲

一、透彻掌握高并发-从理解JVM开始 二、从线程的开闭看JVM的作用 1.run方法 启动start方法,会调用底层C方法,告诉操作系统当前线程处于可运行状态,而如果直接调用run方法,则就不是以线程的方式来运行了,只是当做一个普…