Java HashMap

2025年4月1日 | 阅读14分钟

Java HashMap 类实现了 Map 接口,允许我们存储键值对,其中键应该是唯一的。如果您尝试插入重复键,它将替换相应键的元素。使用键索引进行更新、删除等操作非常方便。HashMap 类位于 java.util 包中。

Java 中的 HashMap 类似于旧的 Hashtable 类,但它不是同步的。它也允许我们存储 null 元素,但只能有一个 null 键。从 Java 5 开始,它表示为 HashMap<K,V>,其中 K 代表键,V 代表值。它继承了 AbstractMap 类并实现了 Map 接口。

注意事项

  • Java HashMap 根据键存储值。
  • Java HashMap 只包含唯一的键。
  • Java HashMap 可能有一个 null 键和多个 null 值。
  • Java HashMap 是非同步的。
  • Java HashMap 不维护顺序。
  • Java HashMap 类的初始默认容量为 16,负载因子为 0.75。

HashMap 的属性

  1. 性能:如果哈希函数将元素适当地分布在桶中,HashMap 对于 put 和 get 等简单操作提供常数时间性能。
  2. 迭代:哈希映射的 entrySet()、keySet() 或 values() 方法通常用于遍历映射并分别检索键、值或键值对。
  3. 重 sizing:当元素数量超过预定阈值时,HashMap 会自动调整大小并重新哈希元素以提供最佳性能。
  4. 负载因子:您可以在哈希映射中设置的负载因子会告诉您映射应相对于其容量何时调整大小。默认的负载因子是 0.75。
  5. 冲突处理:当两个不同的键具有相同的哈希值时,HashMap 会使用链表法来处理哈希冲突,将多个条目存储在同一个桶中并维护一个条目链表。
  6. 快速失败迭代器:HashMap 返回的迭代器是快速失败的,这意味着如果迭代器产生后映射在结构上发生任何更改(除了使用迭代器自身的 remove() 函数),它将抛出 ConcurrentModificationException。

HashMap 类层次结构

Java HashMap

如上图所示,HashMap 类扩展了 AbstractMap 类并实现了 Map 接口。

HashMap 类声明

让我们看一下 java.util.HashMap 类的声明。

HashMap 类参数

让我们看一下 java.util.HashMap 类的参数。

  • K:这是此映射所维护的键的类型。
  • V:这是映射值的类型。

Java HashMap 类的构造函数

构造函数描述
HashMap()用于构造一个默认的 HashMap。
HashMap(Map<? extends K,? extends V> m)用于通过使用给定 Map 对象 m 的元素来初始化哈希映射。
HashMap(int capacity)用于将哈希映射的容量初始化为给定的整数值 capacity。
HashMap(int capacity, float loadFactor)用于通过使用其参数来初始化哈希映射的容量和负载因子。
HashMap(int capacity, float loadFactor, boolean accessOrder)用于指定排序模式并通过使用其输入来初始化哈希映射的容量和负载因子。

Java HashMap 类的方法

方法描述
void clear()用于从此映射中删除所有映射。
boolean isEmpty()如果此映射不包含任何键值映射,则返回 true。
Object clone()返回此 HashMap 实例的浅拷贝:键和值本身不被克隆。
Set entrySet()用于返回此映射中包含的映射的集合视图。
Set keySet()用于返回此映射中包含的键的集合视图。
V put(Object key, Object value)用于将条目插入到映射中。
void putAll(Map map)用于将指定的映射插入到映射中。
V putIfAbsent(K key, V value)仅当尚未指定时,才将指定的键和值插入到映射中。
V remove(Object key)用于删除指定键的条目。
boolean remove(Object key, Object value)它会从映射中删除与指定键关联的指定值。
V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)用于计算指定键及其当前映射值(如果不存在则为 null)的映射,并更新 Map。
V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)如果指定的键尚未与值关联(或映射为 null),则使用给定的映射函数计算其值,并将其输入到此 Map 中,除非结果为 null。
V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)如果指定键的值存在且非 null,则用于计算给定键及其当前映射值的新映射。
boolean containsValue(Object value)如果映射中存在等于该值的某个值,则此方法返回 true,否则返回 false。
boolean containsKey(Object key)如果映射中存在等于该键的某个键,则此方法返回 true,否则返回 false。
boolean equals(Object o)用于将指定的对象与 Map 进行比较。
void forEach(BiConsumer<? super K,? super V> action)对 Map 中的每个条目执行给定的操作,直到所有条目都被处理完毕或操作抛出异常。
V get(Object key)返回与键关联的值的对象。
V getOrDefault(Object key, V defaultValue)返回指定键映射到的值,如果 Map 中没有该键的映射,则返回 defaultValue。
boolean isEmpty()如果映射为空,则此方法返回 true;如果包含至少一个键,则返回 false。
V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)如果指定的键尚未与值关联或与 null 关联,则将其与给定的非 null 值关联。
V replace(K key, V value)替换指定键的指定值。
boolean replace(K key, V oldValue, V newValue)为指定键替换旧值与新值。
void replaceAll(BiFunction<? super K,? super V,? extends V> function)通过调用给定的函数来替换每个条目的值,直到所有条目都被处理完毕或函数抛出异常。
Collection<V> values()返回 Map 中包含的值的 collection 视图。
int size()此方法返回映射中的条目数。

Java HashMap 示例

让我们看一个存储键值对的简单 HashMap 示例。

示例

编译并运行

输出

Iterating Hashmap...
1 Mango
2 Apple
3 Banana
4 Grapes

说明

提供的 Java 代码初始化了一个名为 map 的 HashMap,它被参数化以接受 Integer 键和 String 值。然后使用 put 方法将四个键值对添加到 HashMap 中,将整数 1 到 4 与相应的 фрукты 名称('Mango', 'Apple', 'Banana', and 'Grapes')关联起来。随后,一个循环使用 entrySet() 方法遍历 HashMap 的条目,并将每个键值对打印到控制台。

HashMap 中没有重复键

我们不能在 HashMap 中存储重复键。但是,如果我们尝试用另一个值存储重复键,它将替换该值。

由于这种行为,HashMap 保证保留键和值之间的一对一映射,其中每个键和值都由唯一标识。同样重要的是要记住,HashMap 允许 null 值,但只能存在一个 null 键。在 HashMap 数据结构中,这种对键的唯一限制使得能够快速检索和操作键值对。

示例

编译并运行

输出

Iterating Hashmap...
1 Grapes
2 Apple
3 Banana

说明

提供的 Java 代码初始化了一个名为 map 的哈希映射,将 String 值映射到 Integer 键。使用 put 方法,将四个键值对(每个代表一个水果及其匹配的标识符)添加到 HashMap 中。有趣的是,代码通过尝试引入具有不同值(“Grapes”)的重复键(1)来替换与键 1 关联的值(“Mango”)。

Java HashMap 添加元素的示例

有以下几种方法可以在 HashMap 中添加元素

1. 使用 put() 方法

使用 put() 方法将键值对插入到 HashMap 中。如果指定的键已存在,则覆盖相关值中的新值。

2. 使用 putIfAbsent() 方法

putIfAbsent() 方法仅在给定键尚不存在时才将键值对添加到 HashMap 中。

3. 使用 putAll() 方法

使用 putAll() 方法将一个 HashMap 中的所有键值对复制到另一个 HashMap 中。指定映射中的所有组件都添加到活动映射中。

这些技术使 Java 程序员能够灵活地向 HashMap 添加条目,从而有效地操作和组织键值数据结构。

让我们在 Java 程序中实现以上所有方法。

示例

编译并运行

输出

Initial list of elements: {}
After invoking put() method 
100 Amit
101 Vijay
102 Rahul
After invoking putIfAbsent() method 
100 Amit
101 Vijay
102 Rahul
103 Gaurav
After invoking putAll() method 
100 Amit
101 Vijay
102 Rahul
103 Gaurav
104 Ravi

说明

提供的 Java 代码创建了一个名为 hm 的哈希映射,其键值对分别为 String 和 Integer。然后,使用 put() 方法向此 HashMap 添加新元素。接着,如果给定键尚未与值关联,则使用 putIfAbsent() 函数添加键值对。然后,使用 putAll() 方法将 hm 中的所有组件添加到 map 中,从而创建一个名为 map 的新 HashMap。

Java HashMap 删除元素的示例

1. remove(Object key)

如果 HashMap 中存在给定键的映射,则将其删除。返回与键对应的键,如果键不存在,则返回 null。

2. remove(Object key, Object value)

仅当提供的键当前映射到指定值时,才会删除该键的条目。如果删除成功,则返回 true,否则返回 false。

3. clear()

通过删除所有键值对来清空 HashMap。

示例

编译并运行

输出

Initial list of elements: {100=Amit, 101=Vijay, 102=Rahul, 103=Gaurav}
Updated list of elements: {101=Vijay, 102=Rahul, 103=Gaurav}
Updated list of elements: {102=Rahul, 103=Gaurav}
Updated list of elements: {103=Gaurav}

说明

提供的 Java 代码初始化了一个名为“map”的哈希映射,其中包含整数键和字符串值。然后,它使用 put() 函数将多个键值对填充到映射中。然后使用 remove() 函数演示各种删除场景:首先,根据特定键删除一个条目 (map.remove(100));然后,删除一个基于值的条目 (map.remove(101));最后,根据键和值删除一个条目 (map.remove(102, "Rahul"))。为了反映每次删除操作后所做的修改,会输出哈希映射中条目的更新列表。

Java HashMap 替换元素的示例

  1. replace(K key, V value):如果给定键当前映射到一个值,则此方法才替换该键的条目。如果没有为键建立映射,则返回 null。否则,它返回与给定键关联的先前值。
  2. replace(K key, V oldValue, V newValue):仅当该键当前映射到提供的 oldValue 时,此函数才替换键条目。如果发生替换,则返回 true;否则,返回 false。

示例

编译并运行

输出

Initial list of elements:
100 Amit
101 Vijay
102 Rahul
Updated list of elements:
100 Amit
101 Vijay
102 Gaurav
Updated list of elements:
100 Amit
101 Ravi
102 Gaurav
Updated list of elements:
100 Ajay
101 Ajay
102 Ajay

说明

提供的 Java 代码初始化了一个名为“hm”的哈希映射,其中包含多个整数键和字符串值键值对。之后,它演示了哈希映射中替换元素的各种技术。首先,它使用 replace(K key, V value) 方法将与键 102 相关联的值替换为“Gaurav”。然后,使用 replace(K key, V oldValue, V newValue) 方法将与键 101 相关联的值从“Vijay”更改为“Ravi”。最后,它使用 replaceAll(BiFunction<K, V, V> function) 方法将 HashMap 中的所有值替换为“Ajay”。在每次替换操作之后,都会输出 HashMap 的更新条目列表。

HashSet 和 HashMap 之间的区别

HashSet 只包含值,而 HashMap 包含条目(键和值)。

特性HashSetHashMap
包含仅值键值对(条目)
数据结构实现 Set 接口实现 Map 接口
用途用于存储唯一元素用于存储键值对
Null 元素允许一个 null 元素允许一个 null 键和多个 null 值
排序无序无序
检索通过值直接访问通过键访问
实施内部使用 HashMap 实现内部使用 HashTable 实现
性能通常提供更好的性能性能可能稍低
内存开销较低,因为只存储值较高,因为同时存储键和值
迭代迭代元素很简单迭代条目不太直观
用例检查存在性,维护唯一性将值与键关联,高效检索值
并发性非同步,在没有外部同步的情况下不适合并发访问可以同步或使用 ConcurrentHashMap 进行并发访问
API 差异用于添加、删除和检查包含关系的 方法用于基于键获取/设置值的 方法,以及用于管理键值对的附加操作

让我们看看 Java HashMap 程序。

示例

编译并运行

输出

1 Details:
101 Let us C Yashwant Kanetkar BPB 8
2 Details:
102 Data Communications and Networking Forouzan Mc Graw Hill 4
3 Details:
103 Operating System Galvin Wiley 6

说明

提供的 Java 代码演示了如何使用哈希映射存储和检索 Book 类的实例。它首先定义了 Book 类,该类有一个构造函数来初始化 id、name、author、publisher 和 quantity 属性。在 MapExample 类中,创建了一个带有整数键和 Book 值的哈希映射。代码使用不同的整数键将多个 Book 对象构造并添加到哈希映射中。接着,代码使用 for-each 循环遍历 HashMap 的条目集。代码检索每个条目的键和匹配的 Book 对象,并打印出每个 Book 的详细信息,包括其 id、name、author、publisher 和 quantity。

结论

总而言之,Java HashMap 类提供了一个强大的工具来有效地存储键值对。当哈希函数将元素均匀地分布在存储桶中时,它为 put 和 get 等简单操作提供了常数时间性能。它便于根据键快速插入、检索和删除元素。HashMap 通过负载因子控制、自动重 sizing 和通过独立链表处理冲突等功能,为管理大型数据集提供了灵活性和可伸缩性。重要的是要记住,哈希映射不是同步的,因此在没有外部同步的情况下不适合并发访问。哈希映射是 Java 中的一种基本数据结构,它在各种编程环境中提供了一种灵活的方式来处理键值映射和关联数组。

相关主题

How to iterate Map in Java

How to Sort HashMap in Java

Load Factor in HashMap

Java HashMap 的工作原理 | HashMap 如何工作

HashMap 和 Hashtable 的区别

How to Sort HashMap by Value

HashSet 和 HashMap 之间的区别

HashMap 和 TreeMap 之间的区别

Java Map 接口


Java HashMap 选择题

1) 在平均情况下,get 方法提供什么样的性能?

  1. O(1)
  2. O(log n)
  3. O(n)
  4. O(n log n)
 

答案:a)

解释:在平均情况下,由于高效的哈希机制将条目均匀地分布在存储桶中,get 方法提供常数时间性能,O(1)。


2) 如果两个对象根据 equals 方法是相等的,那么这些对象在 HashMap 中还必须满足什么条件?

  1. 它们的 hashCode 必须不同
  2. 它们的 hashCode 必须相同
  3. 它们的键必须是 null
  4. 它们不能存储在 HashMap 中
 

答案:b)

解释:在 HashMap 中,如果两个对象根据 equals 方法是相等的,那么它们必须具有相同的 hashCode。这确保它们被放置在同一个存储桶中,使 HashMap 能够正确地识别和管理条目。


3) 使用哪个方法来检查 HashMap 中是否存在特定键?

  1. map.containsValue()
  2. map.findKey()
  3. map.containsKey()
  4. map.getKey()
 

答案:c)

解释:containsKey() 方法用于检查 HashMap 中是否存在特定键,如果键存在则返回 true。


4) 如果在 HashMap 中使用了 null 键,会发生什么?

  1. 抛出运行时异常
  2. 键被忽略
  3. 键被视为有效条目
  4. 键被替换为空字符串
 

答案:c)

解释:Java HashMap 允许一个 null 键并将其视为有效条目。null 键被映射到一个特定的存储桶,使其能够像任何其他键一样被存储和检索。


5) 使用哪个方法可以从 HashMap 中删除条目?

  1. map.delete()
  2. map.remove()
  3. map.clearEntry()
  4. map.drop()
 

答案:b)

解释:remove 方法用于根据提供的键从 HashMap 中删除特定条目。它返回与键关联的值,如果键未找到则返回 null。