Java中的HashMap
在Java中,HashMap 是Java 1.2版本后的Java集合的一部分。这个类位于 java.util 包中。它提供了Java Map接口的基本实现。Java中的HashMap将数据存储在(键,值)对中,您可以通过另一种类型(如Integer)的索引访问它们。一个对象用作键(索引)到另一个对象(值)的键。如果您尝试在HashMap中插入重复的键,则会替换相应键的元素。
HashMap是什么?
Java HashMap 与HashTable相似,但它是非同步的。它允许存储null键,但只能有一个null键对象,并且可以有任意数量的null值。这个类不保证映射的顺序。要使用这个类及其方法,您需要导入 java.util.HashMap 包或它的超类。
Java HashMap示例
// Java program to illustrate HashMap class of java.util
// package
// Importing HashMap class
import java.util.HashMap;
// Main class
public class GFG {
// Main driver method
public static void main(String[] args)
{
// Create an empty hash map by declaring object
// of string and integer type
HashMap<String, Integer> map = new HashMap<>();
// Adding elements to the Map
// using standard put() method
map.put("vishal", 10);
map.put("sachin", 30);
map.put("vaibhav", 20);
// Print size and content of the Map
System.out.println("Size of map is:- "
+ map.size());
// Printing elements in object of Map
System.out.println(map);
// Checking if a key is present and if
// present, print value by passing
// random element
if (map.containsKey("vishal")) {
// Mapping
Integer a = map.get("vishal");
// Printing value for the corresponding key
System.out.println("value for key"
+ " \"vishal\" is:- " + a);
}
}
}
输出
Size of map is:- 3
{vaibhav=20, vishal=10, sachin=30}
value for key "vishal" is:- 10
HashMap声明
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>,Cloneable,Serializable
参数:
它有两个参数,分别是:
- 该映射维护的键的类型
- 映射值的类型
Java中的HashMap实现了 Serializable , Cloneable ,Map<;K,V>;接口。Java HashMap扩展了 AbstractMap <K,V> ** 类,它的直接子类有LinkedHashMap和 **PrinterStateReasons 。
Java HashMap在Java中的层次结构

HashMap的特点:
HashMap是一种数据结构,用于基于键存储和检索值。HashMap的一些关键特征包括:
- 访问速度快 : HashMap 可以提供常数时间访问元素,这意味着检索和插入元素非常快,通常是 O(1)的时间复杂度。
- 使用哈希函数 : HashMap 使用哈希函数将键映射到数组中的索引。这允许根据键快速查找值。
- 存储键值对 : HashMap 中的每个元素都由一个键值对组成。键用于查找关联值。
- 支持 null 键和值 : HashMap 允许使用 null 值和键。这意味着可以使用 null 键来存储值,并且可以将 null 值与键关联起来。
- 无序 : HashMap 无序,这意味着不保留向 map 添加元素的顺序。但是,LinkedHashMap 是 HashMap 的一个变种,它保留插入顺序。
- 允许重复 : HashMap 允许重复值,但不允许重复键。如果添加重复键,则将覆盖与该键关联的前一个值。
- 线程不安全 : HashMap 不是线程安全的。这意味着如果多个线程同时访问同一个 hashmap,可能会导致数据不一致。如果需要线程安全性,可以使用 ConcurrentHashMap。
- 容量和负载因子 : HashMap 有一个容量,即它可以容纳的元素数,以及一个负载因子,即在调整大小之前 hashmap 可以容纳的最大数量。
在Java中创建 HashMap:
import java.util.HashMap;
public class ExampleHashMap {
public static void main(String[] args) {
// 创建 HashMap
HashMap<String, Integer> hashMap = new HashMap<>();
// 向 HashMap 中添加元素
hashMap.put("John", 25);
hashMap.put("Jane", 30);
hashMap.put("Jim", 35);
// 访问 HashMap 中的元素
System.out.println(hashMap.get("John")); // 输出:25
// 从 HashMap 中删除元素
hashMap.remove("Jim");
// 检查 HashMap 中是否存在元素
System.out.println(hashMap.containsKey("Jim")); // 输出:false
// 获取 HashMap 的大小
System.out.println(hashMap.size()); // 输出:2
}
}
输出结果
25
false
2
HashMap 中的构造函数
HashMap 提供 4 个构造函数,每个构造函数的访问修饰符均为 public,它们如下所示:
- HashMap()
- HashMap(int initialCapacity)
- HashMap(int initialCapacity, float loadFactor)
- HashMap(Map map)
接下来,一边逐个讨论上述构造函数,一边借助简洁的 Java 程序实现。
1. HashMap()
它是默认构造函数,创建一个 HashMap 的实例,初始容量为 16,负载因子为 0.75。
语法:
HashMap<K, V> hm = new HashMap<K, V>();
示例:
// Java程序演示HashMap()构造函数
// 导入基本所需类
import java.io.*;
import java.util.*;
// 主类
// 向HashMap中添加元素
class GFG {
// 主驱动程序方法
public static void main(String args[])
{
// 不需要两次提及泛型类型
HashMap<Integer, String> hm1 = new HashMap<>();
// 使用泛型初始化HashMap
HashMap<Integer, String> hm2
= new HashMap<Integer, String>();
// 使用put方法添加元素
// 自定义输入元素
hm1.put(1, "one");
hm1.put(2, "two");
hm1.put(3, "three");
hm2.put(4, "four");
hm2.put(5, "five");
hm2.put(6, "six");
// 打印并显示HashMap 1的映射
System.out.println("HashMap hm1的映射是: "
+ hm1);
// 打印并显示HashMap 2的映射
System.out.println("HashMap hm2的映射是: "
+ hm2);
}
}
输出
HashMap hm1的映射是: {1=one, 2=two, 3=three}
HashMap hm2的映射是: {4=four, 5=five, 6=six}
2. HashMap(int initialCapacity)
它创建了一个HashMap实例,具有指定的初始容量和负载因子为0.75。
语法:
HashMap<K, V> hm = new HashMap<K, V>(int initialCapacity);
示例:
// Java程序演示HashMap(int initialCapacity)构造函数
// 导入基本类
import java.io.*;
import java.util.*;
// 主类
// 向HashMap中添加元素
class AddElementsToHashMap {
// 主驱动程序方法
public static void main(String args[])
{
// 不需要两次提及泛型类型
HashMap<Integer, String> hm1 = new HashMap<>(10);
// 使用泛型初始化HashMap
HashMap<Integer, String> hm2
= new HashMap<Integer, String>(2);
// 使用put方法向HashMap对象添加元素
// HashMap 1
hm1.put(1, "one");
hm1.put(2, "two");
hm1.put(3, "three");
// HashMap 2
hm2.put(4, "four");
hm2.put(5, "five");
hm2.put(6, "six");
// 打印HashMap 1的元素
System.out.println("HashMap hm1的映射是: "
+ hm1);
// 打印HashMap 2的元素
System.out.println("HashMap hm2的映射是: "
+ hm2);
}
}
输出
HashMap hm1的映射是: {1=one, 2=two, 3=three}
HashMap hm2的映射是: {4=four, 5=five, 6=six}
3. HashMap(int initialCapacity, float loadFactor)
它创建了一个HashMap实例,具有指定的初始容量和指定的负载因子。
语法:
HashMap<K, V> hm = new HashMap<K, V>(int initialCapacity, float loadFactor);
示例:
// Java程序演示HashMap(int initialCapacity, float loadFactor)构造函数
// 导入基本类
import java.io.*;
import java.util.*;
// 主类
// 向HashMap中添加元素
class AddElementsToHashMap {
// 主驱动程序方法
public static void main(String args[])
{
// 不需要两次提及泛型类型
HashMap<Integer, String> hm1 = new HashMap<>(10, 0.75f);
// 使用泛型初始化HashMap
HashMap<Integer, String> hm2
= new HashMap<Integer, String>(2, 0.5f);
// 使用put方法添加元素到HashMap中
// HashMap 1
hm1.put(1, "one");
hm1.put(2, "two");
hm1.put(3, "three");
// HashMap 2
hm2.put(4, "four");
hm2.put(5, "five");
hm2.put(6, "six");
// 打印HashMap 1的元素
System.out.println("HashMap hm1的映射是: "
+ hm1);
// 打印HashMap 2的元素
System.out.println("HashMap hm2的映射是: "
+ hm2);
}
}
输出
HashMap hm1的映射是: {1=one, 2=two, 3=three}
HashMap hm2的映射是: {4=four, 5=five, 6=six}
// Java program to Demonstrate
// HashMap(int initialCapacity,float loadFactor) Constructor
// 导入基本类
import java.io.*;
import java.util.*;
// 主类
// 向HashMap添加元素
class GFG {
// 主驱动程序方法
public static void main(String args[])
{
// 不需要两次声明泛型类型
HashMap<Integer, String> hm1
= new HashMap<>(5, 0.75f);
// 使用泛型初始化HashMap
HashMap<Integer, String> hm2
= new HashMap<Integer, String>(3, 0.5f);
// 使用put()方法添加元素
// 自定义输入元素
hm1.put(1, "one");
hm1.put(2, "two");
hm1.put(3, "three");
hm2.put(4, "four");
hm2.put(5, "five");
hm2.put(6, "six");
// 在对象hashMap 1中打印和显示元素
System.out.println("Mappings of HashMap hm1 are : "
+ hm1);
// 在对象hashMap 2中打印和显示元素
System.out.println("Mapping of HashMap hm2 are : "
+ hm2);
}
}
输出结果
Mappings of HashMap hm1 are : {1=one, 2=two, 3=three}
Mapping of HashMap hm2 are : {4=four, 5=five, 6=six}
4. HashMap(Map map)
它创建一个与指定映射相同映射的HashMap实例。
HashMap<K, V> hm = new HashMap<K, V>(Map map);
// Java program to demonstrate the
// HashMap(Map map) Constructor
import java.io.*;
import java.util.*;
class AddElementsToHashMap {
public static void main(String args[])
{
// 不需要两次声明泛型类型
Map<Integer, String> hm1 = new HashMap<>();
// 使用put方法添加元素
hm1.put(1, "one");
hm1.put(2, "two");
hm1.put(3, "three");
// 使用泛型初始化HashMap
HashMap<Integer, String> hm2
= new HashMap<Integer, String>(hm1);
System.out.println("Mappings of HashMap hm1 are : "
+ hm1);
System.out.println("Mapping of HashMap hm2 are : "
+ hm2);
}
}
输出结果
Mappings of HashMap hm1 are : {1=one, 2=two, 3=three}
Mapping of HashMap hm2 are : {1=one, 2=two, 3=three}
在HashMap上执行各种操作
1.添加元素
为了向map中添加元素,我们可以使用put()方法。 然而,Hashmap中不保留插入顺序。 在内部,对于每个元素,会生成一个单独的哈希值,并根据此哈希将元素进行索引,使其更高效。
// Java program to traverse
// elements of HashMap
import java.io.*;
import java.util.*;
class TraversalOfHashMap {
public static void main(String[] args)
{
// Creation of a HashMap
HashMap<String, Integer> hm
= new HashMap<String, Integer>();
// Adding elements to the HashMap
hm.put("a", 1);
hm.put("b", 2);
hm.put("c", 3);
hm.put("d", 4);
// Traversal of HashMap
Iterator<Map.Entry< String,
Integer>> iterator
= hm.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry< String, Integer> entry
= (Map.Entry< String, Integer>)iterator.next();
System.out.println("Key = " + entry.getKey()
+ ", Value = " + entry.getValue());
}
}
}
输出
Key = a, Value = 1
Key = b, Value = 2
Key = c, Value = 3
Key = d, Value = 4
// Java程序遍历一个
// Java.util.HashMap
import java.util.HashMap;
import java.util.Map;
public class 遍历HashMap {
public static void main(String[] args)
{
// 初始化HashMap
HashMap<String, Integer> map = new HashMap<>();
// 使用put方法添加元素
map.put("vishal", 10);
map.put("sachin", 30);
map.put("vaibhav", 20);
// 使用for-each循环迭代map
for (Map.Entry<String, Integer> e : map.entrySet())
System.out.println("Key: " + e.getKey()
+ " Value: " + e.getValue());
}
}
输出结果
Key: vaibhav Value: 20
Key: vishal Value: 10
Key: sachin Value: 30
HashMap的重要特点
要访问值,必须知道其键。HashMap被称为HashMap,因为它使用一种称为散列的技术。Hashing是一种将大型字符串转换为表示相同字符串的小字符串的技术。较短的值有助于索引和更快的搜索。HashSet也在内部使用HashMap。
HashMap的一些重要特点是:
- HashMap是java.util包的一部分。
- HashMap扩展了抽象类AbstractMap,它还提供了Map接口的不完整实现。
- 它还实现了Cloneable和Serializable接口。上述定义中的K和V分别表示键和值。
- HashMap不允许重复的键,但允许重复的值。这意味着单个键不能包含多个值,但多个键可以包含单个值。
- HashMap也允许空键,但只有一次和多个空值。
- 此类不保证映射的顺序;特别是,它不保证顺序会随时间保持不变。它与HashTable大致相似,但不是同步的。
HashMap的内部结构
在内部,HashMap包含一个Node数组,而节点表示为包含4个字段的类:
- int hash
- K key
- V value
- Node next
可以看出,节点包含对自己对象的引用。所以它是一个链接列表。
HashMap:

节点:

HashMap的性能
HashMap的性能取决于两个参数,分别是:
- 初始容量
- 负载因子
1. 初始容量 —— HashMap在创建时的容量(HashMap实例化时可以容纳的桶数)。在Java中,初始值为 2^4=16,即可以容纳16个键值对。
2. 负载因子 —— 哈希表容量达到多少比例时就需要增加容量(在填充因子达到某一程度时,重新计算哈希表)。在Java中,默认值为 0.75f,即在填充了75%的容量后会进行重新计算哈希表的操作。
3. 阈值 —— 负载因子与初始容量的乘积。在Java中,默认值为 (16 * 0.75 = 12)。也就是说,在HashMap中插入12个键值对后就会进行重新计算哈希表的操作。
4. 重新计算哈希表 —— 当哈希表达到阈值时,将其容量加倍的操作。在Java中,默认情况下,HashMap会按照以下序列进行重新计算哈希表操作:2^4, 2^5, 2^6, 2^7, ……等等。
如果将初始容量设置得较高,则不会进行重新计算哈希表的操作。但是,由于这样会增加迭代的时间复杂度,所以必须非常灵活地选择初始容量以提高性能。需要考虑预期值的数量来设置初始容量。最常用的负载因子值为0.75,这可以在时间和空间成本之间提供很好的平衡。负载因子的值在0和1之间变化。
注意: 从Java 8开始,Java开始使用自平衡BST来代替链表进行链接。自平衡BST的优点是,它的最坏搜索时间(当每个键映射到相同的槽时)为 O(Log n)。
同步HashMap
正如我们所说,HashMap是不同步的,即多个线程可以同时访问它。如果多个线程同时访问此类并且至少有一个线程在结构上操纵它,则需要在外部将其同步。这可以通过同步封装Map的某个对象来完成。如果没有这样的对象,则可以将其包装在Collections.synchronizedMap()中,以使HashMap同步并避免意外的不同步访问。如下面的例子所示:
Map m = Collections.synchronizedMap(new HashMap(...));
现在Map m是同步的。如果在创建迭代器之后进行任何结构修改(除了通过迭代器的remove方法),此类迭代器将是快速失败的。如果迭代器出错,它将抛出ConcurrentModificationException。
HashMap的时间复杂度:
如果哈希函数正确编写并在桶之间分散元素,则HashMap提供基本操作get和put的常数时间复杂度。HashMap的迭代取决于HashMap的容量和键值对的数量,基本上与容量+大小成正比。容量是HashMap中桶的数量。因此,最好不要在HashMap中初始添加大量的桶。
HashMap的应用:
HashMap主要是hashing的实现。在需要高效的搜索、插入和删除操作时,它非常有用。请参阅哈希的应用程序以获取详细信息。
HashMap中的方法
- K —— 映射到Map中的键的类型。
- V —— 映射到Map中的值的类型。
| 方法 | 描述 |
|---|---|
| clear() | 从此地图中删除所有映射。 |
| clone() | 返回此HashMap实例的浅拷贝:键和值本身不会被克隆。 |
| compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) | 尝试计算指定密钥及其当前映射值(如果没有当前映射,则为null)的映射。 |
| computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) | 如果指定的键尚未与值相关联(或映射到null),则尝试使用给定的映射函数计算其值,并将其输入到此地图中,除非null。 |
| computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) | 如果指定键的值存在且非null,则尝试计算给定键及其当前映射值的新映射。 |
| containsKey(Object key) | 如果此映射包含特定键的映射,则返回true。 |
| containsValue(Object value) | 如果此映射将一个或多个键映射到指定值,则返回true。 |
| entrySet() | 返回此映射中所包含的映射的Set视图。 |
| get(Object key) | 返回映射到指定键的值,如果此地图不包含键的映射,则返回null。 |
| isEmpty() | 如果此地图不包含键值映射,则返回true。 |
| keySet() | 返回此地图中包含的键的Set视图。 |
| merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) | 如果指定的键尚未与值相关联或与null相关联,则将其与给定的非null值相关联。 |
| put(K key, V value) | 将指定的值与该地图中指定的键相关联。 |
| putAll(Map<? extends K, ? extends V> m) | 将指定映射中的所有映射复制到此映射。 |
| remove(Object key) | 如果存在,则从此地图中删除指定键的映射。 |
| size() | 返回此地图中的键值映射数。 |
| values() | 返回此地图中包含的值的Collection视图。 |
继承自java.util.AbstractMap类的方法
| 方法 | 描述 |
|---|---|
| equals() | 将指定对象与此地图进行比较,以检查它们是否相等。 |
| hashCode() | 返回此地图的哈希码值。 |
| toString() | 返回此地图的字符串表示形式。 |
极客教程