如何在Java中反转Map

2023/06/07

1. 概述

在这篇简短的文章中,我们将介绍如何在Java中反转Map。这个想法是为Map<K, V>类型的给定Map创建Map<V, K>的新实例。此外,我们还将了解如何处理源Map中存在重复值的情况。

请参阅我们的另一篇文章以了解有关HashMap类本身的更多信息。

2. 定义问题

假设我们有一个包含几个键值对的Map:

Map<String, Integer> map = new HashMap<>();
map.put("first", 1);
map.put("second", 2);

原始Map将存储以下元素:

{first=1, second=2}

相反,我们希望将键转换为值,将值转换为键。结果将是:

{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()。之后,我们将原来的Value作为新的Key,将原来的Key作为新的Value添加到inversedMap对象中。换句话说,我们通过用值替换键和用键替换值来复制Map的内容。此外,这适用于8之前的Java版本,但我们应该注意,这种方法仅在源Map的值是唯一的情况下才有效

4. 使用Stream API反转Map

Java 8提供了来自Stream API的便捷方法,以更实用的方式反转Map。让我们来看看其中的几个。

4.1 Collectors.toMap()

如果源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;
}

首先,entrySet()被转换为对象流。随后,我们使用Collectors.toMap()将Key和Value收集到inversedMap对象中

让我们考虑源Map包含重复值。在这种情况下,我们可以使用映射函数将自定义规则应用于输入元素:

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()的最后一个参数是一个映射函数。使用它,我们可以自定义在有重复的情况下应该添加哪个键。在上面的示例中,如果源Map包含重复值,我们将为键保留第一个值。但是,如果值重复,我们只能保留一个键。

4.2 Collectors.groupingBy()

有时,即使源Map包含重复值,我们也可能需要所有键。或者,Collectors.groupingBy()可以更好地控制重复值的处理

例如,假设我们有以下键值对:

{first=1, second=2, two=2}

此处,值“2”针对不同的键重复两次。在这些情况下,我们可以使用groupingBy()方法对Value对象实现级联的“group by”操作:

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()收集器将重复值收集到List中,从而生成MultiMap。现在的输出将是:

{1=[first], 2=[two, second]}

5. 总结

在本文中,我们通过示例快速回顾了几种反转HashMap的内置方法。此外,我们还了解了在反转Map对象时如何处理重复值。

同时,一些外部库在Map接口之上提供了额外的功能。我们之前已经演示了如何使用Google Guava BiMapApache BidiMap反转Map。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章