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 是一个高效的键值对存储容器,适用于需要根据键快速查找、插入或删除数据的场景。更多详细内容请关注其他相关文章。