在 Java 中,Map
是一种存储键值对(Key-Value)的集合接口,它不继承自 Collection
接口。Map
的核心特点是键的唯一性,每个键最多映射一个值。以下是关于 Map
的详细介绍:
一、核心特性
- 键唯一性:键不能重复,重复插入会覆盖原有值(通过
equals()
判断键是否相同)。 - 键值关联:每个键映射到一个值,可通过键快速查找对应值。
- 无序性:默认不保证元素顺序(如
HashMap
),但部分实现(如LinkedHashMap
)可保持插入顺序或访问顺序。
二、常见实现类
1. HashMap
- 特性:基于哈希表实现,无序,允许
null
键和null
值(键最多一个null
)。 - 时间复杂度:插入、删除、查找操作均为 O (1)。
- 示例:
java
Map<String, Integer> map = new HashMap<>(); map.put("apple", 1); map.put("banana", 2); map.put("apple", 3); // 键重复,覆盖原值 System.out.println(map); // 输出: {apple=3, banana=2}
2. LinkedHashMap
- 特性:继承自
HashMap
,维护双向链表记录插入顺序或访问顺序(通过构造参数指定)。 - 时间复杂度:基本操作 O (1),但维护顺序会增加开销。
- 示例(插入顺序):
java
Map<String, Integer> map = new LinkedHashMap<>(); map.put("apple", 1); map.put("banana", 2); map.put("cherry", 3); System.out.println(map); // 输出: {apple=1, banana=2, cherry=3}
3. TreeMap
- 特性:基于红黑树实现,键按自然顺序或指定比较器排序,不允许
null
键。 - 时间复杂度:插入、删除、查找操作均为 O (log n)。
- 示例:
java
Map<Integer, String> map = new TreeMap<>(); map.put(3, "C"); map.put(1, "A"); map.put(2, "B"); System.out.println(map); // 输出: {1=A, 2=B, 3=C}
4. Hashtable
- 特性:线程安全(所有方法同步),不允许
null
键或值,性能较低。 - 替代方案:优先使用
ConcurrentHashMap
(线程安全且性能更高)。
三、基本操作
1. 添加 / 更新键值对
java
map.put("key", 100); // 返回旧值(若存在),否则返回null
map.putIfAbsent("key", 200); // 仅当键不存在时添加
2. 获取值
java
int value = map.get("key"); // 返回值,若键不存在则返回null(可能NPE)
int defaultValue = map.getOrDefault("key", 0); // 不存在时返回默认值
3. 删除键值对
java
map.remove("key"); // 返回被删除的值,若键不存在返回null
map.remove("key", 100); // 仅当键对应值为100时删除,返回boolean
4. 判断键 / 值存在
java
boolean hasKey = map.containsKey("key");
boolean hasValue = map.containsValue(100);
5. 集合大小
java
int size = map.size();
boolean isEmpty = map.isEmpty();
四、遍历方式
1. 遍历键(keySet)
java
for (String key : map.keySet()) {
System.out.println("Key: " + key + ", Value: " + map.get(key));
}
2. 遍历值(values)
java
for (int value : map.values()) {
System.out.println("Value: " + value);
}
3. 遍历键值对(entrySet)
效率最高,避免多次查找:
java
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
}
4. Java 8+ forEach()
java
map.forEach((key, value) -> {
System.out.println("Key: " + key + ", Value: " + value);
});
五、常用方法
1. 合并操作
java
map.merge("key", 1, (oldValue, newValue) -> oldValue + newValue);
// 若键不存在,插入(key, 1);若存在,则将原值与1相加后更新
2. 计算操作
java
map.compute("key", (k, v) -> (v == null) ? 1 : v + 1);
// 若键不存在,插入(key, 1);若存在,则将原值加1
3. 批量操作
java
Map<String, Integer> anotherMap = new HashMap<>();
map.putAll(anotherMap); // 将anotherMap的所有键值对添加到map
map.clear(); // 清空map
六、线程安全
- Hashtable:所有方法同步,性能低。
- Collections.synchronizedMap():包装非线程安全的
Map
:java
Map<String, Integer> synchronizedMap = Collections.synchronizedMap(new HashMap<>());
- ConcurrentHashMap:推荐方案,分段锁实现高效并发:
java
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
七、示例代码
java
import java.util.*;
public class MapExample {
public static void main(String[] args) {
// HashMap示例
Map<String, Integer> fruitPrices = new HashMap<>();
fruitPrices.put("apple", 5);
fruitPrices.put("banana", 3);
fruitPrices.put("apple", 6); // 覆盖原值
System.out.println("HashMap: " + fruitPrices); // {apple=6, banana=3}
// TreeMap示例(排序)
Map<Integer, String> numbers = new TreeMap<>();
numbers.put(3, "Three");
numbers.put(1, "One");
numbers.put(2, "Two");
System.out.println("TreeMap: " + numbers); // {1=One, 2=Two, 3=Three}
// 遍历方式
System.out.println("\n遍历entrySet:");
for (Map.Entry<String, Integer> entry : fruitPrices.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// Java 8+ forEach
System.out.println("\nJava 8 forEach:");
fruitPrices.forEach((k, v) -> System.out.println(k + ": " + v));
// 合并操作
fruitPrices.merge("apple", 2, Integer::sum);
System.out.println("\n合并后: " + fruitPrices); // {apple=8, banana=3}
}
}
八、适用场景
- 缓存:通过键快速查找值。
- 统计频率:键为元素,值为出现次数。
- 配置管理:存储键值对配置信息。
选择 Map
实现类时,需根据是否需要排序、保持顺序、线程安全以及性能要求来决定。