在 Java 中创建 HashMap
简介
在 Java 编程中,HashMap
是一个非常重要且常用的数据结构。它实现了 Map
接口,用于存储键值对(key-value pairs),并且允许 null 键和 null 值。HashMap
基于哈希表(hash table)实现,这使得它在查找、插入和删除操作上具有高效性,在许多实际应用场景中发挥着关键作用。本文将深入探讨在 Java 中创建 HashMap
的基础概念、使用方法、常见实践以及最佳实践。
目录
- 基础概念
- 使用方法
- 创建空的
HashMap
- 创建带有初始容量的
HashMap
- 创建带有初始容量和加载因子的
HashMap
- 从另一个
Map
创建HashMap
- 创建空的
- 常见实践
- 添加键值对
- 获取值
- 修改值
- 删除键值对
- 遍历
HashMap
- 最佳实践
- 选择合适的初始容量
- 了解加载因子
- 正确处理键的哈希值
- 小结
- 参考资料
基础概念
HashMap
是 Java 集合框架的一部分,它基于哈希表数据结构。哈希表是一种数据结构,通过计算键的哈希值(hash code)来确定存储位置,从而实现快速查找。每个键值对在哈希表中被称为一个 entry
。
哈希值是一个整数,HashMap
使用这个值来决定将键值对存储在哈希表的哪个桶(bucket)中。当两个不同的键计算出相同的哈希值时,就会发生哈希冲突(hash collision)。HashMap
使用链地址法(separate chaining)来解决哈希冲突,即把冲突的键值对存储在同一个桶中的链表中。
使用方法
创建空的 HashMap
import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
public static void main(String[] args) {
// 创建一个空的 HashMap
Map<String, Integer> hashMap = new HashMap<>();
}
}
在上述代码中,我们使用无参构造函数创建了一个空的 HashMap
,键的类型为 String
,值的类型为 Integer
。
创建带有初始容量的 HashMap
import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
public static void main(String[] args) {
// 创建一个带有初始容量为 16 的 HashMap
Map<String, Integer> hashMap = new HashMap<>(16);
}
}
通过传递一个整数参数作为初始容量,可以创建一个具有指定初始容量的 HashMap
。初始容量表示哈希表中桶的数量。
创建带有初始容量和加载因子的 HashMap
import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
public static void main(String[] args) {
// 创建一个带有初始容量为 16,加载因子为 0.75 的 HashMap
Map<String, Integer> hashMap = new HashMap<>(16, 0.75f);
}
}
加载因子(load factor)是一个阈值,当哈希表中的键值对数量达到 容量 * 加载因子
时,哈希表会自动扩容(resize)。默认的加载因子是 0.75。
从另一个 Map
创建 HashMap
import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
public static void main(String[] args) {
// 创建一个源 Map
Map<String, Integer> sourceMap = new HashMap<>();
sourceMap.put("one", 1);
sourceMap.put("two", 2);
// 从源 Map 创建一个新的 HashMap
Map<String, Integer> hashMap = new HashMap<>(sourceMap);
}
}
通过传递一个现有的 Map
作为参数,可以创建一个包含该 Map
所有键值对的 HashMap
。
常见实践
添加键值对
import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
public static void main(String[] args) {
Map<String, Integer> hashMap = new HashMap<>();
// 添加键值对
hashMap.put("one", 1);
hashMap.put("two", 2);
hashMap.put("three", 3);
}
}
使用 put
方法可以向 HashMap
中添加键值对。如果键已经存在,则会覆盖其对应的值。
获取值
import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
public static void main(String[] args) {
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("one", 1);
hashMap.put("two", 2);
// 获取值
Integer value = hashMap.get("one");
System.out.println("Value for key 'one': " + value);
}
}
使用 get
方法可以根据键获取对应的值。如果键不存在,会返回 null
。
修改值
import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
public static void main(String[] args) {
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("one", 1);
// 修改值
hashMap.put("one", 100);
Integer newValue = hashMap.get("one");
System.out.println("New value for key 'one': " + newValue);
}
}
再次使用 put
方法,将相同的键作为参数传入,即可修改其对应的值。
删除键值对
import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
public static void main(String[] args) {
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("one", 1);
// 删除键值对
hashMap.remove("one");
Integer removedValue = hashMap.get("one");
System.out.println("Value after removing 'one': " + removedValue);
}
}
使用 remove
方法可以根据键删除对应的键值对。
遍历 HashMap
import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
public static void main(String[] args) {
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("one", 1);
hashMap.put("two", 2);
hashMap.put("three", 3);
// 遍历键值对
for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
}
// 遍历键
for (String key : hashMap.keySet()) {
System.out.println("Key: " + key);
}
// 遍历值
for (Integer value : hashMap.values()) {
System.out.println("Value: " + value);
}
}
}
可以使用 entrySet
、keySet
和 values
方法分别遍历键值对、键和值。
最佳实践
选择合适的初始容量
如果预先知道 HashMap
中大致会存储多少个键值对,选择合适的初始容量可以减少哈希表的扩容次数,提高性能。一般来说,初始容量应该设置为大于预期元素数量除以加载因子的最小 2 的幂次方。例如,如果预期存储 100 个元素,加载因子为 0.75,则初始容量应设置为 (int) Math.ceil(100 / 0.75) = 134
,最接近的 2 的幂次方是 128 或 256,为了避免频繁扩容,可选择 256。
了解加载因子
加载因子的选择会影响哈希表的性能。较小的加载因子会减少哈希冲突的概率,但会导致哈希表占用更多的空间;较大的加载因子会增加哈希冲突的可能性,但会减少哈希表的空间占用。默认的加载因子 0.75 是一个在空间和性能之间取得较好平衡的值。在实际应用中,需要根据具体需求来调整加载因子。
正确处理键的哈希值
为了确保 HashMap
的性能,键的 hashCode
方法应该正确实现。良好的 hashCode
实现应该尽量使不同的键产生不同的哈希值,减少哈希冲突。同时,equals
方法也需要正确实现,并且要与 hashCode
方法保持一致。即如果两个对象通过 equals
方法比较相等,那么它们的 hashCode
方法返回值必须相同。
小结
本文详细介绍了在 Java 中创建 HashMap
的各种方法,以及常见的操作实践和最佳实践。通过了解 HashMap
的基础概念、合理选择初始容量和加载因子、正确处理键的哈希值等,可以在实际编程中更高效地使用 HashMap
,提升程序的性能和稳定性。
参考资料
- Oracle Java Documentation - HashMap
- 《Effective Java》 by Joshua Bloch