1. 简介
本文将介绍三种不同的方式来从 Map 中根据值(value)查找对应的键(key)。我们还会分析每种方案的优劣。
如果你对 Map
接口还不太熟悉,可以先看看这篇 HashMap 详解。
2. 迭代法
Java 集合框架中的 Map
接口提供了一个叫 entrySet()
的方法,它会返回一个包含所有键值对的 Set
。
✅ 思路很简单:遍历这个 entry-set,找到值匹配的那个 entry,然后返回其 key:
public <K, V> K getKey(Map<K, V> map, V value) {
for (Entry<K, V> entry : map.entrySet()) {
if (entry.getValue().equals(value)) {
return entry.getKey();
}
}
return null;
}
⚠️ 但要注意,可能存在多个 key 指向同一个 value 的情况。
所以如果你需要找出所有匹配的 key,可以改成这样:
public <K, V> Set<K> getKeys(Map<K, V> map, V value) {
Set<K> keys = new HashSet<>();
for (Entry<K, V> entry : map.entrySet()) {
if (entry.getValue().equals(value)) {
keys.add(entry.getKey());
}
}
return keys;
}
❌ 缺点是即使在前面几轮就找到了全部结果,它也会继续遍历完整个 map,性能略差。
3. 函数式风格(Java 8+)
有了 Lambda 表达式和 Stream API 后,我们可以写得更优雅一些:
public <K, V> Stream<K> keys(Map<K, V> map, V value) {
return map
.entrySet()
.stream()
.filter(entry -> value.equals(entry.getValue()))
.map(Map.Entry::getKey);
}
✅ 返回 Stream
的好处在于灵活性高,调用方可以根据需求决定是要第一个 key 还是全部 key:
Stream<String> keyStream1 = keys(capitalCountryMap, "South Africa");
String capital = keyStream1.findFirst().get();
Stream<String> keyStream2 = keys(capitalCountryMap, "South Africa");
Set<String> capitals = keyStream2.collect(Collectors.toSet());
由于 Stream 是惰性求值的,调用方还能控制实际执行多少次迭代。
4. 使用 Apache Commons Collections
如果某个 Map 要频繁地通过 value 查找 key,每次都遍历一遍显然效率不高。
✅ 更合理的做法是维护一个反向索引的 Map,这样查找就是 O(1) 时间复杂度。
Apache Commons 提供了双向 Map —— BidiMap
,可以直接调用 getKey()
方法:
BidiMap<String, String> capitalCountryMap = new DualHashBidiMap<>();
capitalCountryMap.put("Berlin", "Germany");
capitalCountryMap.put("Cape Town", "South Africa");
String capitalOfGermany = capitalCountryMap.getKey("Germany");
⚠️ 注意:BidiMap
强制要求 key 和 value 都是唯一的,即 1:1 映射关系。如果你尝试插入一个 value 已存在的 entry,它会自动删除旧的那个 entry。
❌ 此外,这种做法占用内存更大,因为它要额外保存一份反向映射。
更多关于 BidiMap
的使用细节可参考 这篇教程。
5. 使用 Google Guava
Guava 中也提供了类似的双向 Map —— BiMap
。它提供了 inverse()
方法用于获取反向 Map:
HashBiMap<String, String> capitalCountryMap = HashBiMap.create();
capitalCountryMap.put("Berlin", "Germany");
capitalCountryMap.put("Cape Town", "South Africa");
String capitalOfGermany = capitalCountryMap.inverse().get("Germany");
⚠️ 和 BidiMap
类似,BiMap
也不允许重复的 value。否则会抛出 IllegalArgumentException
。
❌ 同样,为了维护反向映射,内存开销也不小。
想深入了解 BiMap
的朋友可以看 这篇教程。
6. 总结
本文介绍了几种从 Map 中通过 value 获取 key 的方式,各有优劣:
方式 | 优点 | 缺点 |
---|---|---|
迭代法 | 实现简单 | 性能差 |
函数式 | 灵活、可定制 | 仍需遍历 |
BidiMap / BiMap | 查询快(O(1)) | 内存开销大、限制多 |
选择哪种方式取决于你的具体场景,比如是否频繁查询、是否允许重复值、是否在意内存占用等。
完整代码可以在 GitHub 上找到:GitHub 项目地址