前言
Java 中的 Map 是开发者的 “百宝箱”,但你是否仅仅停留在用 HashMap
储存键值对的阶段?今天就带你解锁 Map 的多种用法,从基础到进阶,教你优雅高效地玩转 Java Map!
一、Map 的常见实现类及特点
在 Java 中,Map 的实现类非常丰富,不同实现类适用于不同场景。
1. HashMap
- 特点:基于哈希表实现,无序,线程不安全。
- 适用场景:大多数单线程场景。
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("Alice", 25);
hashMap.put("Bob", 30);
2. LinkedHashMap
- 特点:保留插入顺序或访问顺序,线程不安全。
- 适用场景:需要按插入顺序遍历或实现 LRU 缓存。
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("Alice", 25);
linkedHashMap.put("Bob", 30);
3. TreeMap
- 特点:基于红黑树实现,按键值自然顺序排序。
- 适用场景:需要有序存储键值对。
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Charlie", 40);
treeMap.put("Alice", 25);
4. ConcurrentHashMap
- 特点:线程安全,支持高并发。
- 适用场景:多线程环境下的高效存取操作。
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("Alice", 25);
二、手把手教你 Map 的高阶用法
1. 用 getOrDefault
提供默认值
传统获取值需要先判断是否包含键:
Integer age = map.containsKey("Alice") ? map.get("Alice") : 0;
现在可以更优雅地用:
Integer age = map.getOrDefault("Alice", 0);
2. 用 putIfAbsent
避免覆盖
场景:如果键不存在才添加值。
map.putIfAbsent("Alice", 25);
这比传统方式更简洁:
if (!map.containsKey("Alice")) {
map.put("Alice", 25);
}
3. 用 merge
轻松合并值
适合对键对应的值进行累加或拼接操作。
map.merge("Alice", 1, Integer::sum);
map.merge("Bob", "hello", (oldValue, newValue) -> oldValue + " " + newValue);
4. 用 computeIfAbsent
延迟初始化
动态生成值并存入 Map:
map.computeIfAbsent("Alice", key -> 0);
例如实现学生成绩按班级分组:
Map<String, List<Integer>> scoresByClass = new HashMap<>();
scoresByClass.computeIfAbsent("Class1", k -> new ArrayList<>()).add(95);
5. 用 compute
灵活更新值
更新值时不需要额外判断键是否存在:
map.compute("Alice", (key, oldValue) -> (oldValue == null) ? 1 : oldValue + 1);
6. 用 forEach
优雅遍历
传统遍历 Map 的代码比较啰嗦:
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
现在可以用 forEach
:
map.forEach((key, value) -> System.out.println(key + " -> " + value));
三、Map 的进阶玩法
1. 用 LinkedHashMap 实现 LRU 缓存
LinkedHashMap
支持按访问顺序排序,非常适合实现 LRU 缓存:
LinkedHashMap<String, String> lruCache = new LinkedHashMap<>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
return size() > 5; // 限制最多存 5 个元素
}
};
lruCache.put("A", "Apple");
lruCache.put("B", "Banana");
lruCache.get("A"); // 访问 A,变为最近访问
2. 用 TreeMap 实现区间查询
TreeMap
的子集视图方法可以实现范围查询:
TreeMap<Integer, String> treeMap = new TreeMap<>();
treeMap.put(1, "A");
treeMap.put(3, "B");
treeMap.put(5, "C");
System.out.println(treeMap.subMap(1, 5)); // 输出 {1=A, 3=B}
3. 用 ConcurrentHashMap 实现并发计数器
在多线程环境下统计数据:
ConcurrentHashMap<String, LongAdder> counter = new ConcurrentHashMap<>();
counter.computeIfAbsent("Alice", k -> new LongAdder()).increment();
System.out.println(counter.get("Alice").sum());
四、性能对比与选型建议
实现类 | 特点 | 适用场景 |
---|---|---|
HashMap | 无序,线程不安全 | 单线程、快速查找 |
LinkedHashMap | 插入/访问有序,线程不安全 | 遍历顺序有要求或实现 LRU 缓存 |
TreeMap | 有序(按键排序) | 需要范围查询或有序存储 |
ConcurrentHashMap | 分段锁,线程安全 | 高并发环境下的读写 |
五、结语
Java 的 Map 远不止储存键值对这么简单,学会这些高阶用法,能让你的代码更优雅、更高效!赶紧试试这些技巧,把你的集合操作提升到新高度吧!
如果本文对你有帮助,请分享给更多人,或者在评论区聊聊你的 Map 使用心得! 😊