1. 概述
在这篇简短的文章中,我们将探讨如何在Java中**反转一个Map
**。目标是为给定的类型为Map<K, V>
的Map
创建一个新的Map<V, K>
实例。此外,我们还将讨论如何处理源映射中存在重复值的情况。
更多关于HashMap
类的信息,请参考我们的另一篇文章。
2. 定义问题
假设我们有一个包含几个键值对的Map
:
Map<String, Integer> map = new HashMap<>();
map.put("first", 1);
map.put("second", 2);
原始的Map
将存储这样的内容:
{first=1, second=2}
相反,我们希望将键反转成值,反之亦然,得到一个新的Map
对象。结果将是:
{1=first, 2=second}
3. 使用传统for循环
首先,让我们看看如何使用**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;
}
在这里,我们遍历Map
对象的entrySet()
。然后,我们将原始值作为新键,原始键作为新值添加到inversedMap
对象中。换句话说,我们通过替换键和值来复制映射的内容。这种方法适用于Java 8之前的版本,但请注意,这种方法仅在源映射的值唯一时才有效。
4. 使用Stream API反转Map
Java 8提供了Stream API中的便捷方法,以更函数式的方式反转Map
。让我们来看看其中的一些方法。
4.1. 使用Collectors.toMap()
如果源映射中没有重复值,我们可以使用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;
}
首先,将entrySet()
转换为对象流。然后,我们使用Collectors.toMap()
收集键和值到inversedMap
对象中。
如果源映射包含重复值,我们可以使用映射函数来应用自定义规则到输入元素:
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)
);
}
在这个方法中,Collectors.toMap()
的最后一个参数是一个映射函数。使用它,我们可以定制在值重复时应添加哪个键。在上述示例中,如果源映射包含重复值,我们保留第一个值作为键。然而,如果我们只想保留一个键,可以修改这个规则。
4.2. Collectors.groupingBy()
有时,即使源映射包含重复值,我们也可能需要所有键。在这种情况下,Collectors.groupingBy()
提供了更好地处理重复值的控制。
例如,考虑以下键值对:
{first=1, second=2, two=2}
这里的值“2”对应不同的键重复两次。在这种情况下,我们可以使用groupingBy()
方法执行对值对象的级联“分组”操作:
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;
}
解释一下,Collectors.mapping()
函数使用指定的收集器对给定键关联的值执行归约操作。groupingBy()
收集器将重复值收集到一个列表中,从而得到一个多映射。输出现在将是:
{1=[first], 2=[two, second]}
5. 总结
在这篇文章中,我们快速回顾了几种内置方法,以及它们在例子中的使用,来反转HashMap
。我们也看到了在反转Map
对象时如何处理重复值。
同时,一些外部库在Map
接口之上提供了额外的功能。我们之前已经演示了如何使用Google Guava的BiMap
和Apache的BidiMap
来反转Map
。
如往常一样,这些示例的代码可在GitHub上找到:https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-collections-maps-5。