Java 集合之Map
·
Java Map 接口深度解析:键值对存储的艺术
Map 是 Java 集合框架中最重要、最常用的接口之一,它表示键值对(Key-Value)的映射关系,提供了高效的数据存储和检索能力。Map 接口及其实现类是处理键值对数据的核心工具。
一、Map 接口核心特性
1. 基本特性
- 键值对存储:每个元素包含一个键(Key)和一个值(Value)
- 键唯一性:键不能重复,值可以重复
- 无序性:大多数实现不保证顺序(TreeMap/LinkedHashMap除外)
- 非线程安全:除 ConcurrentHashMap 和 Hashtable 外
2. 核心方法
// 基本操作
V put(K key, V value); // 添加键值对
V get(Object key); // 获取值
V remove(Object key); // 删除键值对
boolean containsKey(Object key); // 检查键是否存在
boolean containsValue(Object value); // 检查值是否存在
// 集合视图
Set<K> keySet(); // 返回键的Set视图
Collection<V> values(); // 返回值的Collection视图
Set<Map.Entry<K, V>> entrySet(); // 返回键值对的Set视图
// 批量操作
void putAll(Map<? extends K, ? extends V> m); // 添加所有元素
void clear(); // 清空Map
// 大小信息
int size(); // 元素数量
boolean isEmpty(); // 是否为空
二、主要实现类对比
| 特性 | HashMap | LinkedHashMap | TreeMap | ConcurrentHashMap | Hashtable |
|---|---|---|---|---|---|
| 底层结构 | 数组+链表/红黑树 | 链表+哈希表 | 红黑树 | 分段锁+哈希表 | 数组+链表 |
| 顺序保证 | 无序 | 插入/访问顺序 | 键排序 | 无序 | 无序 |
| null键/值 | 允许/允许 | 允许/允许 | 不允许/允许 | 不允许/不允许 | 不允许/不允许 |
| 线程安全 | 否 | 否 | 否 | 是 | 是(已过时) |
| 性能(O(1)) | 是 | 是 | 否(O(log n)) | 是 | 是 |
| 扩容机制 | 2倍 | 2倍 | 无 | 分段扩容 | 2倍+1 |
| 同步方式 | - | - | - | CAS+synchronized | synchronized |
三、核心实现类详解
1. HashMap(最常用实现)
// 创建HashMap
Map<String, Integer> map = new HashMap<>();
// 添加元素
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);
// 获取元素
int age = map.get("Bob"); // 30
// 遍历方式1:键集合
for (String name : map.keySet()) {
System.out.println(name + ": " + map.get(name));
}
// 遍历方式2:键值对集合(推荐)
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 遍历方式3:Java8 forEach
map.forEach((k, v) -> System.out.println(k + ": " + v));
// 重要特性:允许null键值
map.put(null, 0);
map.put("Unknown", null);
2. LinkedHashMap(保持插入顺序)
// 创建LinkedHashMap(保持插入顺序)
Map<String, Integer> linkedMap = new LinkedHashMap<>();
linkedMap.put("First", 1);
linkedMap.put("Second", 2);
linkedMap.put("Third", 3);
// 遍历顺序与插入顺序一致
linkedMap.forEach((k, v) -> System.out.println(k)); // First, Second, Third
// 创建访问顺序的LinkedHashMap(LRU缓存)
Map<String, Integer> lruCache = new LinkedHashMap<>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, Integer> eldest) {
return size() > 3; // 最大容量3
}
};
lruCache.put("A", 1);
lruCache.put("B", 2);
lruCache.put("C", 3);
lruCache.get("A"); // 访问A,使其成为最新
lruCache.put("D", 4); // 移除最老的B
System.out.println(lruCache.keySet()); // [C, A, D]
3. TreeMap(键排序)
// 创建TreeMap(自然顺序)
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Orange", 5);
treeMap.put("Apple", 3);
treeMap.put("Banana", 4);
// 自动按键排序
treeMap.forEach((k, v) -> System.out.println(k)); // Apple, Banana, Orange
// 自定义排序
Map<String, Integer> customOrder = new TreeMap<>(Comparator.reverseOrder());
customOrder.put("A", 1);
customOrder.put("B", 2);
customOrder.put("C", 3);
customOrder.forEach((k, v) -> System.out.println(k)); // C, B, A
// 范围查询
String firstKey = treeMap.firstKey(); // "Apple"
String lastKey = treeMap.lastKey(); // "Orange"
Map<String, Integer> subMap = treeMap.subMap("B", "O"); // Banana
4. ConcurrentHashMap(高并发Map)
// 创建ConcurrentHashMap
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
// 线程安全操作
concurrentMap.put("A", 1);
concurrentMap.putIfAbsent("A", 2); // 不会覆盖
// 原子操作
concurrentMap.compute("A", (k, v) -> v == null ? 0 : v + 1); // A=2
concurrentMap.merge("B", 1, Integer::sum); // B=1
// 并行操作(Java8+)
concurrentMap.forEach(2, // 并行度
(k, v) -> System.out.println(k + ":" + v), // 转换函数
System.out::println // 消费者
);
四、Map 高级操作(Java 8+)
1. 计算操作
Map<String, Integer> map = new HashMap<>();
// 不存在时添加
map.computeIfAbsent("Alice", k -> 25); // Alice=25
// 存在时更新
map.computeIfPresent("Alice", (k, v) -> v + 1); // Alice=26
// 合并值
map.merge("Bob", 30, (oldVal, newVal) -> oldVal + newVal); // Bob=30
map.merge("Bob", 5, Integer::sum); // Bob=35
2. 批量操作
// 替换所有值
map.replaceAll((k, v) -> v * 2); // 所有值翻倍
// 获取或默认值
int age = map.getOrDefault("Unknown", -1); // -1
// 删除条件
map.remove("Alice", 25); // 仅当Alice=25时删除
3. 流式处理
// 过滤并收集
Map<String, Integer> filtered = map.entrySet().stream()
.filter(e -> e.getValue() > 30)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
// 转换值
List<String> names = map.keySet().stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
五、Map 性能优化
1. HashMap 调优
// 预分配容量(避免扩容)
int expectedSize = 1000;
float loadFactor = 0.75f;
Map<String, Integer> map = new HashMap<>((int)(expectedSize / loadFactor) + 1, loadFactor);
// 优化哈希函数
class CustomKey {
private final String id;
@Override
public int hashCode() {
return Objects.hash(id); // 使用Objects.hash
}
@Override
public boolean equals(Object obj) {
// 实现equals
}
}
2. TreeMap 优化
// 使用高效Comparator
Comparator<String> efficientComparator = Comparator.comparing(String::length)
.thenComparing(Comparator.naturalOrder());
Map<String, Integer> optimizedTreeMap = new TreeMap<>(efficientComparator);
3. ConcurrentHashMap 优化
// 预估并发级别
ConcurrentHashMap<String, Integer> chm = new ConcurrentHashMap<>(16, 0.75f, 8);
// 使用批量操作
chm.search(2, (k, v) -> v > 30 ? k : null); // 并行搜索
六、Map 设计模式应用
1. 对象注册表
public class ServiceRegistry {
private final Map<String, Service> services = new ConcurrentHashMap<>();
public void register(String name, Service service) {
services.put(name, service);
}
public Service getService(String name) {
return services.get(name);
}
}
2. 缓存实现
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int maxSize;
public LRUCache(int maxSize) {
super(maxSize, 0.75f, true);
this.maxSize = maxSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxSize;
}
}
3. 配置管理
public class Configuration {
private final Map<String, String> configMap = new TreeMap<>();
public void loadConfig(Properties props) {
props.forEach((k, v) -> configMap.put((String)k, (String)v));
}
public String getConfig(String key) {
return configMap.get(key);
}
}
七、Map 常见问题与解决方案
1. 并发修改异常
// 错误示例
for (String key : map.keySet()) {
if (key.startsWith("test")) {
map.remove(key); // 抛出ConcurrentModificationException
}
}
// 解决方案1:使用迭代器
Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Integer> entry = it.next();
if (entry.getKey().startsWith("test")) {
it.remove(); // 安全删除
}
}
// 解决方案2:Java8+ removeIf
map.keySet().removeIf(key -> key.startsWith("test"));
2. 自定义对象作为键
class CustomKey {
private final String id;
private final int version;
public CustomKey(String id, int version) {
this.id = id;
this.version = version;
}
// 必须重写equals和hashCode
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CustomKey that = (CustomKey) o;
return version == that.version && Objects.equals(id, that.id);
}
@Override
public int hashCode() {
return Objects.hash(id, version);
}
}
// 使用
Map<CustomKey, String> customMap = new HashMap<>();
customMap.put(new CustomKey("A", 1), "Value");
3. 值类型转换
Map<String, Object> mixedMap = new HashMap<>();
mixedMap.put("age", 25); // Integer
mixedMap.put("name", "Alice"); // String
// 安全转换
public <T> T getValueAs(Map<String, Object> map, String key, Class<T> type) {
Object value = map.get(key);
return type.isInstance(value) ? type.cast(value) : null;
}
Integer age = getValueAs(mixedMap, "age", Integer.class);
八、Map 最佳实践
1. 选择正确的Map实现
- 通用场景:HashMap
- 保持插入顺序:LinkedHashMap
- 键排序需求:TreeMap
- 高并发环境:ConcurrentHashMap
- LRU缓存:LinkedHashMap(访问顺序)
- 线程安全遗留系统:Hashtable(不推荐新代码使用)
2. 性能优化技巧
- 为HashMap预设初始容量和负载因子
- 使用不可变对象作为键
- 避免在键对象中使用慢速hashCode()
- 使用entrySet()遍历而非keySet()+get()
- 在Java8+中使用compute/merge方法
3. 防御性编程
// 返回不可修改的Map视图
public Map<String, Integer> getReadOnlyView() {
return Collections.unmodifiableMap(internalMap);
}
// 深拷贝Map
public static <K, V> Map<K, V> deepCopy(Map<K, V> original) {
Map<K, V> copy = new HashMap<>();
original.forEach((k, v) -> copy.put(k, v)); // 浅拷贝值
return copy;
}
九、Map 在Java新版本中的演进
Java 8
- forEach 方法
- compute, merge 等方法
- getOrDefault
- 流式API支持
Java 9
- 工厂方法创建不可变Map
Map<String, Integer> immutable = Map.of("A", 1, "B", 2);
Map<String, Integer> larger = Map.ofEntries(
Map.entry("C", 3),
Map.entry("D", 4)
);
Java 10
- var 关键字简化声明
var map = new HashMap<String, Integer>();
Java 14+
- record 类型作为键
record Person(String name, int age) {}
Map<Person, String> personMap = new HashMap<>();
Map 接口及其实现类是 Java 开发中最常用、最强大的工具之一。掌握各种 Map 实现的特点、适用场景和高级用法,能够显著提高代码质量和性能。
更多推荐



所有评论(0)