1. Overview
1.概述
In this quick article, we’ll take a look at how to invert a Map in Java. The idea is to create a new instance of Map<V, K> for a given map of type Map<K, V>. In addition, we’ll also see how to handle the case where there are duplicate values present in the source map.
在这篇文章中,我们将看看如何在Java中反转Map。我们的想法是为一个给定的Map<V, K>类型的地图创建一个新的Map<K, V>的实例。此外,我们还将看到如何处理源地图中存在重复值的情况。
Please refer to our other article to learn more about the HashMap class itself.
请参考我们的另一篇文章,以了解更多关于HashMap类本身。
2. Defining the Problem
2.界定问题
Let’s consider we have a Map with a few Key-Value pairs:
让我们考虑一下,我们有一个Map,有几个Key-Value对。
Map<String, Integer> map = new HashMap<>();
map.put("first", 1);
map.put("second", 2);
The original Map would store the items like:
原先的Map将存储像这样的项目。
{first=1, second=2}
Instead, we’d like to invert the keys into values and vice versa into a new Map object. The result would be:
相反,我们想把键转换成值,反之亦然到一个新的Map对象。其结果将是。
{1=first, 2=second}
3. Using a Traditional for Loop
3.使用传统的for循环
First, let’s see how to invert a Map using a for loop:
首先,让我们看看如何使用for循环来反转一个Map。
public static <V, K> Map<V, K> invertMapUsingForLoop(Map<K, V> map) {
Map<V, K> inversedMap = new HashMap<V, K>();
for (Entry<K, V> entry : map.entrySet()) {
inversedMap.put(entry.getValue(), entry.getKey());
}
return inversedMap;
}
Here, we’re iterating through the entrySet() of the Map object. After that, we add the original Value as a new Key and the original Key as the new Value into the inversedMap object. In other words, we copy the contents of the map by replacing the keys with values and values with keys. Further, this is suitable for Java versions before 8, though we should note that this approach only works if the source map’s values are unique.
在这里,我们正在迭代Map对象的entrySet() 。之后,我们将原来的Value作为新的Key加入到inversedMap对象中,并将原来的Key作为新的Value。换句话说,我们通过用值替换键,用键替换值来复制地图的内容。此外,这适用于 8 之前的 Java 版本,不过我们应该注意到,这种方法只有在源地图的值是唯一的情况下才有效。
4. Using Stream API to Invert a Map
4. Using Stream API to Invert a Map
Java 8 provides convenient methods from the Stream API to invert a Map in a more functional style. Let’s have a look at a few of them.
Java 8从Stream API中提供了方便的方法,以更实用的风格反转Map。让我们来看看其中的几个。
4.1. Collectors.toMap()
4.1.Collectors.toMap()
We can use Collectors.toMap() if we don’t have any duplicate values in the source map:
我们可以使用Collectors.toMap() 如果我们在源地图中没有任何重复的值。
public static <V, K> Map<V, K> invertMapUsingStreams(Map<K, V> map) {
Map<V, K> inversedMap = map.entrySet()
.stream()
.collect(Collectors.toMap(Entry::getValue, Entry::getKey));
return inversedMap;
}
First, the entrySet() is converted into a stream of objects. Subsequently, we used Collectors.toMap() to collect the Key and Value into the inversedMap object.
首先,entrySet()被转换为一个对象流。随后,我们使用Collectors.toMap()来收集Key和Value到inversedMap对象。
Let’s consider that the source map contains duplicate values. In such cases, we can use a mapping function to apply custom rules to the input elements:
让我们考虑一下,源映射包含重复的值。在这种情况下,我们可以使用一个映射函数来对输入元素应用自定义规则。
public static <K, V> Map<V, K> invertMapUsingMapper(Map<K, V> sourceMap) {
return sourceMap.entrySet()
.stream().collect(
Collectors.toMap(Entry::getValue, Entry::getKey, (oldValue, newValue) -> oldValue)
);
}
In this method, the last argument to Collectors.toMap() is a mapping function. Using this, we can customize which key should be added in case there are duplicates. In the above example, we retain the first value as key if the source map contains duplicate values. However, we can retain only one key if the values repeat.
在这个方法中,Collectors.toMap()/em>的最后一个参数是一个映射函数。利用这个,我们可以自定义在有重复值的情况下应该添加哪个键。在上面的例子中,如果源地图包含重复的值,我们会保留第一个值作为键。然而,如果数值重复,我们可以只保留一个键。
4.2. Collectors.groupingBy()
4.2.Collectors.groupingBy()
Sometimes, we may need all the keys even if the source map contains duplicate values. Alternatively, Collectors.groupingBy() provides better control for handling duplicate values.
有时,即使源地图包含重复的值,我们也可能需要所有的键。另外,Collectors.groupingBy()为处理重复的值提供了更好的控制。
For instance, let’s consider we have the following Key–Value pair:
例如,让我们考虑我们有以下键–值对。
{first=1, second=2, two=2}
Here, the value “2” is repeated twice for different keys. In these cases, we can use the groupingBy() method to implement a cascaded “group by” operation on the Value objects:
这里,值 “2 “在不同的键上重复了两次。在这些情况下,我们可以使用groupingBy() 方法来实现对Value对象的级联 “分组 “操作。
private static <V, K> Map<V, List<K>> invertMapUsingGroupingBy(Map<K, V> map) {
Map<V, List<K>> inversedMap = map.entrySet()
.stream()
.collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList())));
return inversedMap;
}
To explain a bit, the Collectors.mapping() function performs a reduction operation on the values associated with the given key using the specified collector. The groupingBy() collector collects duplicate values into a List, resulting in a MultiMap. The output now will be:
稍微解释一下,Collectors.mapping()函数使用指定的收集器对与给定键相关的值执行还原操作。groupingBy()收集器将重复的值收集到一个List中,结果是一个MultiMap。现在的输出将是。
{1=[first], 2=[two, second]}
5. Conclusion
5.总结
In this article, we quickly reviewed several built-in ways to invert a HashMap with examples. Also, we saw how to handle duplicate values when we invert a Map object.
在这篇文章中,我们快速回顾了反转HashMap的几种内置方法,并列举了一些例子。此外,我们还看到了当我们反转Map对象时如何处理重复的值。
Meanwhile, a few external libraries provide additional features on top of the Map interface. We’ve previously demonstrated how to invert a Map using Google Guava BiMap and Apache BidiMap.
同时,一些外部库在Map接口之上提供了额外的功能。我们之前已经演示了如何使用Google Guava BiMap和Apache BidiMap来反转一个Map。
As always, the code for these examples is available over on GitHub.
像往常一样,这些例子的代码可以在GitHub上找到over。