Java HashMap
HashMap
是 Java 集合框架中实现了 Map
接口的一个类,它基于哈希表(HashTable
)实现,用来存储键值对(key-value)。在 HashMap
中,键(key)是唯一的,而值(value)可以重复。HashMap
提供了高效的查找、插入和删除操作,通常用于需要根据键快速查找值的场景。
1. HashMap 的基本特性
- 基于哈希表实现:
HashMap
使用哈希表来存储数据,它利用键的hashCode()
方法来决定元素存储的桶位置。 - 键的唯一性:
HashMap
中的每个键(key)是唯一的。如果向HashMap
中插入一个已存在的键,则新的值会替换掉原有的值。 - 允许
null
值和null
键:HashMap
允许一个null
键和多个null
值。 - 无序性:
HashMap
中的元素没有固定的顺序,元素的顺序依赖于其哈希值。 - 线程不安全:
HashMap
本身是非线程安全的,多个线程同时访问和修改HashMap
可能会导致数据不一致。
2. HashMap 的常用构造函数
// 默认构造函数,创建一个空的 HashMap,默认初始容量为 16,负载因子为 0.75
HashMap<K, V> map = new HashMap<>();
// 使用指定的初始容量构造 HashMap
HashMap<K, V> map = new HashMap<>(int initialCapacity);
// 使用指定的初始容量和负载因子构造 HashMap
HashMap<K, V> map = new HashMap<>(int initialCapacity, float loadFactor);
// 基于另一个 Map 创建 HashMap
HashMap<K, V> map = new HashMap<>(Map<? extends K, ? extends V> m);
3. HashMap 常用方法
3.1 添加元素
put(K key, V value)
:将指定的键值对插入HashMap
中。如果键已经存在,则更新其对应的值。
map.put("Apple", 1); // 添加键值对
map.put("Banana", 2); // 添加键值对
map.put("Apple", 3); // 更新 "Apple" 的值为 3
3.2 删除元素
remove(Object key)
:根据指定的键删除键值对,返回被删除的值。如果键不存在,返回null
。
map.remove("Apple"); // 删除 "Apple" 键的对应值
3.3 查询元素
get(Object key)
:根据指定的键获取对应的值,如果键不存在则返回null
。
int value = map.get("Apple"); // 返回 "Apple" 对应的值
containsKey(Object key)
:检查HashMap
是否包含指定的键。
boolean contains = map.containsKey("Banana"); // 判断是否包含键 "Banana"
containsValue(Object value)
:检查HashMap
是否包含指定的值。
boolean containsValue = map.containsValue(3); // 判断是否包含值 3
3.4 修改元素
replace(K key, V value)
:如果键存在,则更新其对应的值并返回旧值;如果键不存在,则返回null
。
map.replace("Banana", 5); // 更新 "Banana" 的值为 5
replace(K key, V oldValue, V newValue)
:仅在值等于oldValue
时,才会替换为newValue
,返回true
或false
。
boolean replaced = map.replace("Banana", 2, 5); // 仅在值为 2 时,替换为 5
3.5 其他常用方法
size()
:返回HashMap
中的键值对数。
int size = map.size(); // 获取元素个数
isEmpty()
:判断HashMap
是否为空。
boolean empty = map.isEmpty(); // 判断是否为空
clear()
:清空HashMap
中的所有键值对。
map.clear(); // 清空所有元素
keySet()
:返回HashMap
中所有键的集合。
Set<K> keys = map.keySet(); // 获取所有键
values()
:返回HashMap
中所有值的集合。
Collection<V> values = map.values(); // 获取所有值
entrySet()
:返回HashMap
中所有键值对的Set
集合,使用Map.Entry
对象。
Set<Map.Entry<K, V>> entrySet = map.entrySet(); // 获取所有键值对
3.6 遍历 HashMap
- 使用 for-each 循环:
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
- 使用 Iterator:
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> entry = iterator.next();
System.out.println(entry.getKey() + ": " + entry.getValue());
}
3.7 默认值处理
getOrDefault(Object key, V defaultValue)
:如果指定的键存在,则返回对应的值,否则返回defaultValue
。
int value = map.getOrDefault("Apple", 0); // 如果 "Apple" 存在,则返回其值,否则返回 0
4. HashMap 的性能
- 查找、插入、删除:
HashMap
的常规操作(如查找、插入、删除)时间复杂度是 O(1),但如果发生哈希冲突,操作时间复杂度可能会退化为 O(n)。 - 负载因子和容量:
HashMap
的性能受初始容量和负载因子的影响。负载因子是一个衡量哈希表“满”程度的参数,默认值为 0.75。负载因子较大时会减少扩容的次数,但会增加哈希冲突的概率。 - 扩容时会将容量翻倍,并重新计算每个元素的哈希值。
- 选择适当的负载因子可以有效平衡时间复杂度和空间复杂度。
5. HashMap 与其他 Map 的比较
特性 | HashMap | TreeMap | LinkedHashMap |
---|---|---|---|
底层实现 | 哈希表 | 红黑树 | 哈希表 + 双向链表 |
键值对顺序 | 无序 | 有序(按自然顺序或自定义顺序) | 按插入顺序或访问顺序有序 |
性能 | O(1)(查找、插入、删除) | O(log n)(查找、插入、删除) | O(1)(查找、插入、删除) |
是否允许 null 键/值 | 允许 1 个 null 键和多个 null 值 | 不允许 null 键 | 允许 1 个 null 键和多个 null 值 |
6. 线程安全
HashMap
不是线程安全的。如果多个线程同时访问 HashMap
,可能会导致数据不一致。如果需要线程安全的实现,可以使用以下方法:
- 使用
Collections.synchronizedMap()
将HashMap
包装为线程安全的Map
。 - 使用
ConcurrentHashMap
,它是线程安全的,并且支持更高的并发性能,适用于多线程环境。
Map<String, Integer> synchronizedMap = Collections.synchronizedMap(new HashMap<>());
7. 使用 HashMap 的场景
HashMap
适合用于以下场景:
- 快速查找和更新数据:如果需要根据某个唯一的键快速查找、更新或删除数据,
HashMap
是一个很好的选择。 - 去重操作:可以用
HashMap
来判断一个元素是否出现过,如果key
存在,表示已经出现过,否则表示第一次出现。 - 实现缓存:当需要根据键快速查找缓存中的数据时,
HashMap
是一种理想的选择。
小结
HashMap
是一个高效的键值对存储容器,适用于需要根据键快速查找、插入或删除数据的场景。更多详细内容请关注其他相关文章。