Java Map通过值来获取键的正确姿势
本文将展示3种,Java中通过Map的值获取其键的方式。本文将讨论不同方法的优缺点。如果想学习Map的更多内容,参见 The Java HashMap Under the Hood
一、Java API
方法1: 迭代方式
Java 集合框架的Map类提供了 entrySet()方法,该方法返回Map的键值对Entry对象。
该方法的思路是,迭代Entry集合,当值和传入的value匹配时,返回对应的key。
public K getKey(Map map, V value) {
for (Entry entry : map.entrySet()) {
if (entry.getValue().equals(value)) {
return entry.getKey();
return null;
}
然而,有可能有多个键对应同一个值。因此我们找到匹配的值时需要将其加入到Set中,Set包含所有待查找的Key。
public Set getKeys(Map map, V value) {
Set keys = new HashSet<>();
for (Entry entry : map.entrySet()) {
if (entry.getValue().equals(value)) {
keys.add(entry.getKey());
return keys;
}
尽管这种方式非常简单而直接,但是采用这种方式即使经过几次迭代就可以找到所有的键也得迭代完整个Map。
方法2: 函数式查找
我可以采用Java8的Lambda表达式,来更灵活和可读地方式实现类似功能。
我们可以使用Stream的map函数,返回满足条件的Entry的键。
public Stream keys(Map map, V value) {
return map
.entrySet()
.stream()
.filter(entry -> value.equals(entry.getValue()))
.map(Map.Entry::getKey);
}
返回键的Stream是为了方便后续多样化的处理方式。调用者或许只需要一个或者所有指向某个值的键。因为Stream是惰性求值的,调用方可以根据需要控制迭代的次数。
另外,使用合适的收集器(collector)可以将返回值转换成需要的集合形式。
Stream keyStream1 = keys(capitalCountryMap, "South Africa");
String capital = keyStream1.findFirst().get();
Stream keyStream2 = keys(capitalCountryMap, "South Africa");
Set capitals = keyStream2.collect(Collectors.toSet());
二、利用Apache Commons Collections库
如果需要多次调用上述方法来查询某个值对应的键,会造成不必要的多次迭代。
在这种场景下,维护另外一个值指向键的map就很有必要了,因为这样可以使通过值获取键的时间复杂度降为常数级。
Apache 的 Commons Collections 库里提供了双向Map叫BidiMap。该类提供了getKey函数来根据值获取键。
BidiMap capitalCountryMap = new DualHashBidiMap<>();