1. Introduction
1.绪论
In this quick tutorial, we’re going to demonstrate three different approaches for retrieving the key from a map for a given value. We’ll also discuss the positives and negatives of the various solutions.
在这个快速教程中,我们将演示三种不同的方法,以从地图中检索给定值的键。我们还将讨论各种解决方案的正面和负面因素。
To learn more about the Map interface, you can check out this article.
要了解有关Map接口的更多信息,你可以查看这篇文章。
2. An Iterative Approach
2.迭代的方法
The Map interface of Java Collections offers a method called entrySet(). It returns all the entries or key-value pairs of the map in a Set.
Java Collections的Map接口提供了一个名为entrySet()的方法。它在一个Set中返回地图的所有条目或键值对。
The idea is to iterate over this entry-set and return the key for which the value matches the supplied value:
我们的想法是在这个条目集上进行迭代,并返回值与提供的值相匹配的键:。
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;
}
However, there might be a possibility that multiple keys are pointing to the same value.
然而,可能会出现多个键指向同一个值的情况。
In that case, if a matching value is found, we add the key to a Set and continue the loop. In the end, we return the Set containing all the desired keys:
在这种情况下,如果找到一个匹配的值,我们就把这个键添加到一个Set中,然后继续循环。最后,我们返回包含所有所需键的Set。
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;
}
Although this is a very straight-forward implementation, it compares all the entries even if all matches are found after a few iterations.
尽管这是一个非常直接的实现,它比较了所有的条目,即使在几个迭代之后找到了所有的匹配。。
3. A Functional Approach
3.功能性的方法
With the introduction of Lambda Expressions in Java 8, we can do it in a more flexible and readable way. We convert the entry-set to a Stream and supply a lambda to filter only those entries with the given value.
随着Java 8中Lambda表达式的引入,我们可以用一种更灵活、更可读的方式来实现。我们将条目集转换为Stream,并提供一个lambda,只过滤那些具有给定值的条目。
Then we use the map method to return a Stream of the keys from the filtered entries:
然后,我们使用map方法来返回一个Stream,即过滤后的条目的键。
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);
}
The advantage of returning a stream is that it can cater to a wide range of client needs. The calling code may require only one key or all the keys pointing to the supplied value. As the evaluation of a stream is lazy, the client can control the number of iteration based on its requirement.
返回流的优势在于它可以满足广泛的客户端需求。调用代码可能只需要一个键或所有指向所提供值的键。由于流的评估是懒惰的,客户端可以根据其要求控制迭代的次数。
Additionally, the client may convert the stream to any collection using an appropriate collector:
此外,客户端可以使用适当的收集器将流转换为任何集合。
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());
4. Using Apache Commons Collections
4.使用Apache Commons集合
The above ideas wouldn’t be very helpful if we need to call the functions very frequently for a particular map. It will unnecessarily iterate the set of its keys again and again.
如果我们需要非常频繁地调用某个地图的函数,那么上述想法就没有什么用处了。它将不必要地一次又一次地迭代其键的集合。
In this scenario, maintaining another map of value to the keys would make more sense as it will take constant time to retrieve the key for a value.
在这种情况下,维护另一个值与键的映射会更有意义,因为检索一个值的键需要持续的时间。
The Commons Collections library by Apache provides with such a bi-directional Map called BidiMap. It has a method named getKey() for retrieving a key for a given value:
Apache的Commons Collections库提供了这样一个双向的Map,叫做BidiMap。它有一个名为getKey()的方法,用于检索一个给定值的键。
BidiMap<String, String> capitalCountryMap = new DualHashBidiMap<>();
capitalCountryMap.put("Berlin", "Germany");
capitalCountryMap.put("Cape Town", "South Africa");
String capitalOfGermany = capitalCountryMap.getKey("Germany");
However, BidiMap imposes a 1:1 relationship between its keys and values. If we try to put a key-value pair for which the value already exists in the Map, it removes the old entry. In other words, it updates the key against the value.
然而,BidiMap在其键和值之间施加了1:1的关系。如果我们试图在Map中放入一个键值已经存在的键值对,它会删除旧的条目。换句话说,它根据值来更新键。
Also, it requires a larger amount of memory for keeping the reverse map.
另外,它需要较大的内存来保存反向地图。
More details on how to use a BidiMap are in this tutorial.
关于如何使用BidiMap的更多细节,见本教程。
5. Using Google Guava
5.使用Google Guava
We may use another bi-directional Map called BiMap found in Guava developed by Google. This class provides a method named inverse() to get the value-key Map or the reverse Map to fetch the key based on a given value:
我们可以使用另一个双向的Map,叫做BiMap,在Google开发的Guava中发现。这个类提供了一个名为inverse()的方法来获取值-键Map或反向Map来获取基于指定值的键。
HashBiMap<String, String> capitalCountryMap = HashBiMap.create();
capitalCountryMap.put("Berlin", "Germany");
capitalCountryMap.put("Cape Town", "South Africa");
String capitalOfGermany = capitalCountryMap.inverse().get("Germany");
Like BidiMap, BiMap also doesn’t allow multiple keys referring to the same value. If we try to make such an attempt, it throws a java.lang.IllegalArgumentException.
与BidiMap一样,BiMap也不允许多个键指向同一个值。如果我们试图进行这样的尝试,它会抛出一个java.lang.IllegalArgumentException。
Needless to say, BiMap also uses a significant amount of memory as it has to store the inverse map inside. If you are interested to know more about BiMap, you can check out this tutorial.
不用说,BiMap也使用了大量的内存,因为它必须在里面存储逆向地图。如果你有兴趣了解更多关于BiMap的信息,你可以查看这个教程。
6. Conclusion
6.结语
In this brief article, we’ve discussed some methods of retrieving a Map’s key given the value. Each approach has its own pros and cons. We should always consider the use-cases and choose the most appropriate one based on the situation.
在这篇简短的文章中,我们讨论了一些检索Map的键给定值的方法。每种方法都有自己的优点和缺点。我们应该始终考虑使用情况,并根据情况选择最合适的方法。
The complete source code for the above tutorial is available over on GitHub.
上述教程的完整源代码可在GitHub上找到。