前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >HashMap的工作原理-Java快速进阶教程

HashMap的工作原理-Java快速进阶教程

作者头像
jack.yang
发布于 2025-04-05 03:13:36
发布于 2025-04-05 03:13:36
15503
代码可运行
举报
运行总次数:3
代码可运行

Java中的HashMap基本上是一个桶数组(也称为HashMap的桶表-bucket table),其中每个桶使用链表来保存元素。链表是节点列表,其中每个节点都包含一个键值对。

简单来说,存储桶是节点的链接列表,其中每个节点都是类 Node<K,V> 的对象。节点的键用于获取哈希值,该哈希值用于从存储桶表中查找存储桶。

HashMap 的工作原理是散列数据结构或技术,该技术使用对象的哈希码将该对象放置在map中。

哈希涉及存储桶、哈希函数(hashCode() 方法)和哈希值。它为对象的插入和检索提供了 O(1) 的最佳时间复杂度。

因此,它是最适合存储键值对的数据结构,日后你可以从中以最短的时间检索回你的内容。

下图显示了用于存储键值对的典型哈希数据结构。

Java HashMap 的初始容量和负载因子


负载因子和初始容量是两个重要因素,它们在Java的HashMap的内部扮演着非常重要角色。

初始容量是在创建 HashMap 时通过 HashMap 在内部衡量存储桶数量或存储桶数组大小的指标。

HashMap 的默认初始容量为 16(即存储桶数量)。它总是以 2(2、4、8、16 等)的幂表示,指数可以 1 至 30 之间变化,所以最大值可以去到2^30。

负载因子是 HashMap 内部用来确定何时需要增加存储桶数组大小的一个因素。默认情况下,它是 0.75。

当 HashMap 中的节点数超过总容量的 75% 时,HashMap 会增加其存储桶数组大小。每次需要增加 HashMap 的存储桶数组大小时,HashMap 的容量总是翻倍。

存储桶表:

一个桶数组称为 HashMap 的桶表。在存储桶表中,存储桶是节点的链接列表,其中每个节点都是类 Node<K、V> 的对象。

节点的键用于获取哈希值,该哈希值用于从放置键值对的存储桶表中计算存储桶的索引。

节点:

链表的每个节点都是类 Node<K,V> 的对象。此类是 HashMap 类的静态内部类,实现 Map.Entry<K,V> 接口。

HashMap 的内部静态节点类的一般语法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}
代码语言:javascript
代码运行次数:0
运行
复制

final int hash:它是键的哈希值。

final K key:它是节点的键。

V value:它给出节点的值。

Node<K,V> next:它指示指向存储桶或链表中存在的下一个节点的指针。

hashCode() 和 equals() 方法在 Java HashMap 内部工作中的作用


hashcode() 和 equals() 在 Java 中 HashMap 的内部工作中起着重要作用。因此,在了解 HashMap 的内部工作原理之前,您应该了解 hashCode() 和 equals() 方法的基本知识。

hashcode():

哈希函数是将键映射到哈希表中的索引的函数。它从键获取索引,并使用该索引检索键的值。

哈希函数首先将搜索键(对象)转换为整数值(称为哈希代码),然后将哈希代码压缩为哈希表的索引。

Java 的对象类(根类)提供了一个其他类需要重写的hashCode()方法。hashCode() 方法用于检索对象的哈希代码。默认情况下,它返回一个整数哈希代码,该代码是对象的内存地址(内存引用)。

hashCode() 方法的一般语法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public native hashCode()

The general syntax to call hashCode() method is as follows:
   int h = key.hashCode();

从 hashCode() 方法获取的值用作存储桶索引号。存储桶索引号是映射中条目(元素)的地址。如果键为 null,则 hashCode() 返回的哈希值将为 0。

equals():

equals()方法是Object类的一个方法,用于检查两个对象是否相等。HashMap使用equals()方法比较key是否相等。

Object类的equals()方法可以被重写。如果重写equals()方法,则必须重写hashCode()方法。

put 操作:Hashmap 的 put() 方法在 Java 内部是如何工作的?


HashMap 的 put() 方法用于存储键值对。put() 方法添加键/值对的语法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
hashmap.put(key, value);

让我们举一个例子,我们将在 HashMap 中插入三个键、值对。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
HashMap<String, Integer> hmap = new HashMap<>();  
 
 hmap.put("John", 20);  
 hmap.put("Harry", 5);  
 hmap.put("Deep", 10);

让我们了解这些“键值对”将会被存放到 HashMap 的哪些索引位置上。

当我们调用 put() 方法将“键值对”添加到 hashmap 时,HashMap 通过调用其 hashCode() 方法来计算键的哈希值或哈希代码。HashMap 使用该代码来计算将在其中放置键/值对的存储桶索引。

计算桶索引的公式(其中 n 是桶数组的大小)如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Index = hashCode(key) & (n-1);

假设“John”的哈希代码值为 2657860。那么“约翰”的索引值为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Index = 2657860 & (16-1) = 4

值 4 就是经过计算后得到的索引值,因此这个“键值对”将被存放到 HashMap 的4号位置上。

注意:由于 HashMap 只允许一个空键。因为 null 的哈希码始终为 0,因此 hashCode(key) 方法返回的哈希值将为 0。

哈希冲突是如何发生和解决的?


当 hashCode() 方法为哈希表中的两个或多个键生成相同的索引值时,会发生哈希冲突。为了克服这个问题,HashMap使用了链表技术。

当 hashCode() 方法为新键生成的索引值和哈希表中已存在的键相同时,HashMap 将使用已经包含链表形式的节点的相同存储桶索引。

在链接列表的最后创建一个新节点,并通过LinkedList将该节点对象连接到现有节点对象。因此,两个key将存储在相同的索引值中。

当使用现有键插入新值对象时,HashMap 会将旧值替换为与键相关的当前值。为此,HashMap 使用 equals() 方法。

此方法检查两个键是否相等。如果 Keys 相同,则此方法返回 true,并且该节点的值将替换为当前值。

下面我们举一例子来详细说明之前已经讲解过的知识

第 1 步:创建一个空的哈希映射,如下所示

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Map map = new HashMap();

HashMap 的默认大小为 16,作为以下大小为 16 的空数组。

您可以看到上图,最初数组中没有元素。

第 2 步:插入第一个元素键值对,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
map.put(new Key("Dinesh"), "Dinesh");

步骤将按如下方式执行:

  1. 首先,它将计算Key{“Dinesh”} 的哈希代码。假定调用hashCode方法,生成哈希代码为 4501。
  2. 使用生成的哈希码计算索引,假定根据索引计算公式(此公式在前文介绍过),计算后等到它的结果是5。
  3. 接着我们创建一个节点对象,如下所示: { int hash = 4501 Key key = {"Dinesh"} Integer value = "Dinesh" Node next = null }
  4. 此节点将放置在索引 5 处。截至目前,索引中并不存在此节点,因为它是第一个元素。

让我们看看下面的 HashMap 图:

第 3 步:添加另一个元素键值对,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
map.put(new Key("Anamika"), "Anamika");

步骤按如下方式执行:

  1. 首先,它将计算Key  {“Anamika”} 的哈希代码。调用hashCode() 方法后得到哈希代码值为 4498。
  2. 使用生成的哈希码计算索引,根据索引计算公式,它将为2。
  3. 现在,它创建一个节点对象,如下所示: { int hash = 4498 Key key = {"Anamika"} Integer value = "Anamika" Node next = null }
  4. 此节点将放置在索引 2 处。截至目前,此索引中并不存在此节点,因为它同样是本Key的第一个元素。

让我们看看下面的 HashMap 图:

第 4 步:(碰撞情况)添加另一个元素键值对,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
map.put(new Key("Arnav"), "Arnav");

步骤按如下方式执行:

  1. 首先,它将计算Key   {“Arnav”} 的哈希代码。调用hashCode() 方法后得到哈希代码值为 4498。
  2. 使用生成的哈希码计算索引,根据索引计算公式,它将为2。
  3. 现在,它创建一个节点对象,如下所示: { int hash = 4498 Key key = {"Arnav"} Integer value = "Arnav" Node next = null }
  4. 如果没有其他对象,则此节点将放置在索引 2 处。
  5. 但是在这个索引 2 中,已经出现了一个节点,所以这是冲突的情况。
  6. 现在,它将检查hashCode()和equals()方法,如果两个键相同,那么它将用当前值替换旧值。
  7. 如果两个键不同,那么它将通过链表将此节点连接到前一个节点对象后面,并且两者都存储在索引 2 中。

让我们看看下面的 HashMap 图:

第 5 步:添加另一个元素键值对,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
map.put(new Key("Rushika"), "Rushika");

步骤按如下方式执行:

  1. 首先,它将计算Key   {“Rushika”}的哈希代码。调用hashCode() 方法后得到哈希代码值为 4515。
  2. 使用生成的哈希码计算索引,根据索引计算公式,它将是3。
  3. 现在,它创建一个节点对象,如下所示: { int hash = 4515 Key key = {"Rushika"} Integer value = "Rushika" Node next = null }
  4. 此节点将放置在索引 3 处。截至目前,此索引中不存在任何节点,因为它是第一个元素。

让我们看看下面的 HashMap 图:

通过上面这个例子,我相信大家已经基本单了解了HashMap 的 put() 方法的内部工作原理。让我们转到下一节,看看 HashMap 的 get() 方法在内部是如何工作的。

HashMap 中的 get() 方法如何在 Java 内部工作?


HashMap 中的 get() 方法用于通过其键检索值。如果我们不知道KEY,它将不会获取值。调用 get() 方法的语法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
value = hashmap.get(key);

当使用 get(K Key) 方法来获取值时,它使用上述方法计算存储桶的索引。然后,使用 equals() 方法搜索该存储桶的列表以查找给定的键,并返回最终结果。

同样本节也举一个例子来帮助理解这个方法工作过程

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
map.get(new Key("Arnav"));

Get将按如下步骤来处理上面这个获取指令:

  1. 首先,它将计算Key{“Arnav”} 的哈希代码。调用hashCode() 方法后得到哈希代码值为 4498。
  2. 使用生成的哈希码计算索引,根据索引计算公式,它将为2。
  3. 转到数组的索引 2,并将第一个元素的键与给定键进行比较。如果两者都相等,则返回该值,否则,检查下一个元素是否存在。
  4. 在我们的例子中,它比较后第一个元素后并不相等,因此只能沿着链表往下找。节点对象的第一个元素和下一个元素不为空。
  5. 如果节点的下一个为空,则返回空。
  6. 如果节点的下一个不为 null,则遍历链表,一直到最后链值。在查找过程中找到则返回实际链点值;直到最后如何都找不到值,则返回空。

put() 和 get() 方法的时间复杂度


HashMap 在常量时间O(1)内插入和检索一个键值对。但在最坏的情况下,当所有节点返回相同的哈希值并插入到同一个存储桶中时,它可能是 O(n)。

n 个节点的遍历开销为 O(n)。

重新散列的概念


重新哈希是当映射中的键数达到阈值时由 HashMap 自动发生的过程。阈值计算为阈值 = 容量 *(负载系数为 0.75)。

在这种情况下,将创建一个具有更多容量的新大小的存储桶阵列,并将所有现有内容复制到其中。

例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Load Factor: 0.75
Initial Capacity: 16 (Available Capacity initially)
Threshold = Load Factor * Available Capacity = 0.75 * 16 = 12

当第 13 个键值对插入 HashMap 时,HashMap 会将其存储桶数组大小增加到 16*2 = 32。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Now Available capacity: 32

Threshold = Load Factor * Available Capacity = 0.75 * 32 = 24

下次将第 25 个键值对插入 HashMap 时,HashMap 会将其存储桶数组大小增加到 32*2 = 64,依此类推。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2008-06-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
【STM32H7教程】第76章 STM32H7的FMC总线应用之驱动AD7606(8通道同步采样, 16bit, 正负10V)
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第76章       STM32H7的FMC总线应用之驱动AD7606
Simon223
2020/05/13
2.5K0
【STM32H7教程】第76章    STM32H7的FMC总线应用之驱动AD7606(8通道同步采样, 16bit, 正负10V)
【STM32H7教程】第74章 STM32H7的SPI总线应用之驱动DAC8563(双通道,16bit分辨率,正负10V)
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第74章       STM32H7的SPI总线应用之驱动DAC856
Simon223
2020/04/10
2K0
【STM32H7教程】第74章  STM32H7的SPI总线应用之驱动DAC8563(双通道,16bit分辨率,正负10V)
基于STM32H7的ADS1256驱动案例,8通道,24bit ADC,带可编程增益(2021-09-20)
V7-068_ADS1256(8通道带PGA的24位ADC).7z (3.12MB) 测试效果: 测试LM285-2.5V稳压效果,抖动40uV:
Simon223
2021/09/26
2K0
基于STM32H7的ADS1256驱动案例,8通道,24bit ADC,带可编程增益(2021-09-20)
【STM32H7教程】第75章 STM32H7的SPI总线应用之驱动DAC8501(双路输出,16bit分辨率,0-5V)
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第75章       STM32H7的SPI总线应用之驱动DAC850
Simon223
2020/05/09
1.7K0
【STM32H7教程】第75章    STM32H7的SPI总线应用之驱动DAC8501(双路输出,16bit分辨率,0-5V)
【STM32H7教程】第73章 STM32H7的SPI总线应用之驱动W25QXX(支持查询,中断和DMA)
本章节为大家讲解标准SPI接线方式驱动W25QXX,实现了查询,中断和DMA三种方式。
Simon223
2020/03/20
2.8K0
【STM32H7教程】第94章 STM32H7的SPI总线应用之双机通信(DMA方式)
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第94章       STM32H7的SPI总线应用之双机通信(DMA
Simon223
2022/05/10
2K0
【STM32H7教程】第94章    STM32H7的SPI总线应用之双机通信(DMA方式)
【STM32H7教程】第46章 STM32H7的ADC应用之DMA方式多通道采样
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第46章       STM32H7的ADC应用之DMA方式多通道采样
Simon223
2020/02/14
3.5K0
【STM32F407开发板用户手册】第33章 STM32F407的SPI总线应用之驱动DAC8563
最新教程下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255 第33章       STM32F407的SPI总线应用之驱动DAC856
Simon223
2020/08/02
1.2K0
【STM32H7教程】第79章 STM32H7的QSPI总线应用之驱动W25QXX(支持查询和MDMA)
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第79章 STM32H7的QSPI总线应用之驱动W25QX
Simon223
2020/11/24
2.7K0
【STM32H7教程】第79章       STM32H7的QSPI总线应用之驱动W25QXX(支持查询和MDMA)
【STM32H7教程】第45章 STM32H7的ADC应用之定时器触发配合DMA双缓冲
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第45章       STM32H7的ADC应用之定时器触发配合DMA
Simon223
2020/02/13
1.9K0
【STM32F407开发板用户手册】第34章 STM32F407的SPI总线应用之驱动DAC8501
最新教程下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255 第34章       STM32F407的SPI总线应用之驱动DAC850
Simon223
2020/08/02
8960
【STM32F407开发板用户手册】第35章 STM32F407的FSMC总线应用之驱动AD7606(8通道同步采样, 16bit, 正负10V)
最新教程下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255 第35章       STM32F407的FSMC总线应用之驱动AD760
Simon223
2020/08/04
5.1K1
【STM32F407开发板用户手册】第35章       STM32F407的FSMC总线应用之驱动AD7606(8通道同步采样, 16bit, 正负10V)
【STM32H7教程】第41章 STM32H7的BDMA应用之控制任意IO做PWM和脉冲数控制
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第41章       STM32H7的BDMA应用之控制任意IO做PW
Simon223
2020/01/13
1.2K0
【STM32H7教程】第41章  STM32H7的BDMA应用之控制任意IO做PWM和脉冲数控制
【STM32H7教程】第30章 STM32H7的USART应用之八个串口FIFO实现
完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第30章       STM32H7的USART应用之八个串口F
Simon223
2019/07/22
3.2K1
【STM32H7教程】第19章 STM32H7的GPIO应用之按键FIFO
完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第19章       STM32H7的GPIO应用之按键FIFO
Simon223
2019/05/19
1.8K0
【STM32H7教程】第19章    STM32H7的GPIO应用之按键FIFO
【STM32H7教程】第37章 STM32H7的LPTIM低功耗定时器应用之PWM
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第37章       STM32H7的LPTIM低功耗定时器应用之PW
Simon223
2020/01/14
1.3K0
【STM32H7教程】第37章    STM32H7的LPTIM低功耗定时器应用之PWM
【STM32H7教程】第48章 STM32H7的FMC总线应用之是32路高速IO扩展
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第48章       STM32H7的FMC总线应用之是32路高速IO
Simon223
2020/02/17
8470
【STM32H7教程】第20章 STM32H7的GPIO应用之无源蜂鸣器
蜂鸣器主要有电磁式和电压式两种,而且都有无源蜂鸣器和有源蜂鸣器两类。开发板使用的是电磁式有源蜂鸣器,而有源和无源的区别是有源蜂鸣器内部自带振荡器,给个电压就能发声,但频率是固定的,只能发出一种声音,而无源蜂鸣器频率可控,给个方波才可以发声,并且根据不同频率发出不同的声音效果。
Simon223
2019/05/19
1.6K0
【STM32H7教程】第18章 STM32H7的GPIO应用之跑马灯
本章教程为大家介绍STM32H7的GPIO应用之跑马灯,跑马灯作为经典的测试例程,可以让大家对STM32H7应用有个简单的整体认识。
Simon223
2019/05/15
8910
【STM32H7教程】第34章 STM32H7的定时器应用之TIM1-TIM17的PWM实现
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第34章       STM32H7的定时器应用之TIM1-TIM17
Simon223
2019/11/30
1.4K0
推荐阅读
【STM32H7教程】第76章 STM32H7的FMC总线应用之驱动AD7606(8通道同步采样, 16bit, 正负10V)
2.5K0
【STM32H7教程】第74章 STM32H7的SPI总线应用之驱动DAC8563(双通道,16bit分辨率,正负10V)
2K0
基于STM32H7的ADS1256驱动案例,8通道,24bit ADC,带可编程增益(2021-09-20)
2K0
【STM32H7教程】第75章 STM32H7的SPI总线应用之驱动DAC8501(双路输出,16bit分辨率,0-5V)
1.7K0
【STM32H7教程】第73章 STM32H7的SPI总线应用之驱动W25QXX(支持查询,中断和DMA)
2.8K0
【STM32H7教程】第94章 STM32H7的SPI总线应用之双机通信(DMA方式)
2K0
【STM32H7教程】第46章 STM32H7的ADC应用之DMA方式多通道采样
3.5K0
【STM32F407开发板用户手册】第33章 STM32F407的SPI总线应用之驱动DAC8563
1.2K0
【STM32H7教程】第79章 STM32H7的QSPI总线应用之驱动W25QXX(支持查询和MDMA)
2.7K0
【STM32H7教程】第45章 STM32H7的ADC应用之定时器触发配合DMA双缓冲
1.9K0
【STM32F407开发板用户手册】第34章 STM32F407的SPI总线应用之驱动DAC8501
8960
【STM32F407开发板用户手册】第35章 STM32F407的FSMC总线应用之驱动AD7606(8通道同步采样, 16bit, 正负10V)
5.1K1
【STM32H7教程】第41章 STM32H7的BDMA应用之控制任意IO做PWM和脉冲数控制
1.2K0
【STM32H7教程】第30章 STM32H7的USART应用之八个串口FIFO实现
3.2K1
【STM32H7教程】第19章 STM32H7的GPIO应用之按键FIFO
1.8K0
【STM32H7教程】第37章 STM32H7的LPTIM低功耗定时器应用之PWM
1.3K0
【STM32H7教程】第48章 STM32H7的FMC总线应用之是32路高速IO扩展
8470
【STM32H7教程】第20章 STM32H7的GPIO应用之无源蜂鸣器
1.6K0
【STM32H7教程】第18章 STM32H7的GPIO应用之跑马灯
8910
【STM32H7教程】第34章 STM32H7的定时器应用之TIM1-TIM17的PWM实现
1.4K0
相关推荐
【STM32H7教程】第76章 STM32H7的FMC总线应用之驱动AD7606(8通道同步采样, 16bit, 正负10V)
更多 >
LV.2
这个人很懒,什么都没有留下~
目录
  • hashCode() 和 equals() 方法在 Java HashMap 内部工作中的作用
  • put 操作:Hashmap 的 put() 方法在 Java 内部是如何工作的?
  • 哈希冲突是如何发生和解决的?
  • HashMap 中的 get() 方法如何在 Java 内部工作?
  • put() 和 get() 方法的时间复杂度
  • 重新散列的概念
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档