在Java中反转HashMap的键和值

93

这是一个简单的问题,我有一个简单的HashMap,我想翻转键和值。

HashMap<Character, String> myHashMap = new HashMap<Character, String>();
myHashMap.put('a', "test one");
myHashMap.put('b', "test two");

我想创建一个新的HashMap,在其中放入相反的内容。

HashMap<String, Character> reversedHashMap = new HashMap<String, Character>();
e.g. Keys "test one" & "test two" and values 'a' & 'b'.

1
简单的问题,简单的答案。你能告诉我们你卡在哪里了吗? - Jeroen Vannevel
2
首先,您必须确保您的值是唯一的。否则,它应该是 Map<String,List<Character>> - Alexis C.
好的,我对JAVA还比较新,不知道从哪里开始……它们都是独一无二的,是的。 - Ken
2
这并非总是可以做到而不会丢失数据。 想象一下你有这个:a -> test one; b -> test two; c -> test one(在你的第一个映射中)。 你将在第二个(输出)映射中得到什么?类似于这样:test one -> a(或c);test two -> b。因此,第一个映射有3个条目,第二个映射只有2个。 - peter.petrov
12个回答

156

它们都是独一无二的,是的

如果您确定您的值是唯一的,您可以遍历旧映射的条目。

Map<String, Character> myNewHashMap = new HashMap<>();
for(Map.Entry<Character, String> entry : myHashMap.entrySet()){
    myNewHashMap.put(entry.getValue(), entry.getKey());
}

或者,您可以使用像 Guava 提供的双向映射,并使用 inverse() 方法:

BiMap<Character, String> myBiMap = HashBiMap.create();
myBiMap.put('a', "test one");
myBiMap.put('b', "test two");

BiMap<String, Character> myBiMapInversed = myBiMap.inverse();

随着的推出,你也可以这样做:

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

Map<Integer, String> mapInversed = 
    map.entrySet()
       .stream()
       .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey))

最后,我向质子背包库添加了我的贡献,其中包含用于Stream API的实用方法。有了这个,你可以像这样做:
Map<Character, String> mapInversed = MapStream.of(map).inverseMapping().collect();

3
感谢您对双向映射使用的解释。我之前不知道这个,现在对我很有用! - Erçin Akçay
当我在使用您的Java-8方法与收集器时,我在“Map.Entry :: getValue”和“getKey”上遇到以下错误:无法从静态上下文中引用非静态方法。 - Jan
1
Jan,这是因为你的map与mapInversed变量声明的不同的泛型类型。 - Marcin
如果值重复了怎么办?你如何处理它? - Chaitanya Uttarwar
@ChaitanyaUttarwar 你需要找到一种合并策略,将与原始映射中相同值相关联的不同键映射起来(你可以将它们分组成列表、求和、根据你想要实现的目标只取一个键)。 - Alexis C.

31

Apache commons collections库提供了一个反转Map的实用方法。如果你确定myHashMap的值是唯一的,你可以使用它。

org.apache.commons.collections.MapUtils.invertMap(java.util.Map map)

示例代码

HashMap<String, Character> reversedHashMap = MapUtils.invertMap(myHashMap) 

28

如果值不是唯一的,安全地反转映射的方法是使用Java 8的groupingBy函数

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

Map<Integer, List<String>> mapInversed = 
map.entrySet()
   .stream()
   .collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList())))

3
我写了一个更简单的循环,它也可以运行(请注意,我的所有值都是唯一的):
HashMap<Character, String> myHashMap = new HashMap<Character, String>();
HashMap<String, Character> reversedHashMap = new HashMap<String, Character>();

for (char i : myHashMap.keySet()) {
    reversedHashMap.put(myHashMap.get(i), i);
}

2
private <A, B> Map<B, A> invertMap(Map<A, B> map) {
    Map<B, A> reverseMap = new HashMap<>();
    for (Map.Entry<A, B> entry : map.entrySet()) {
        reverseMap.put(entry.getValue(), entry.getKey());
    }
    return reverseMap;
}

记住,put 方法在使用相同的键调用时会替换值。因此,如果映射中有两个键具有相同的值,则只有一个键会存在于反向映射中。


1
我认为说明如何处理重复值成为键会很有用。 - Cooper Buckingham
@CHBuckingham 同意了 - Mark Hetherington

2

使用以下示例片段进行测试,尝试使用MapUtils和Java8 Stream功能。 它在两种情况下都有效。

public static void main(String[] args) {
    Map<String, String> test = new HashMap<String, String>();
    test.put("a", "1");
    test.put("d", "1");
    test.put("b", "2");
    test.put("c", "3");
    test.put("d", "4");
    test.put("d", "41");

    System.out.println(test);

    Map<String, String> test1 = MapUtils.invertMap(test);

    System.out.println(test1);

    Map<String, String> mapInversed = 
            test.entrySet()
               .stream()
               .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

    System.out.println(mapInversed);
}

Output:
{a=1, b=2, c=3, d=41}
{1=a, 2=b, 3=c, 41=d}
{1=a, 2=b, 3=c, 41=d}

2

遍历键和值的列表,然后将它们相加。

HashMap<String, Character> reversedHashMap = new HashMap<String, Character>();
for (String key : myHashMap.keySet()){
    reversedHashMap.put(myHashMap.get(key), key);
}

6
entrySet更好,无需在每个get上计算哈希码。 - A4L
@A4L 这是真的,但对于简单的任务来说,差别不大。 - hichris123

2
回答你的问题,你可以从你的地图中获取 entrySet,然后使用 getValue作为key getKey作为value,将它放入新的地图中。
但请记住, Map中的键是唯一的,这意味着如果你的原始地图中有一个值对应两个不同的键,那么在新的地图中只会保留第二个键(按迭代顺序)。

1

使用Java 8中引入的forEach方法

Map<Short, String> regularMap = new HashMap<>();
Map<String, Short> inversedMap = new HashMap<>();

regularMap.forEach((key, value) -> inversedMap.put(value, key));

0

Java:
简单的方法,不需要Java 8

Map<String,String> map=new HashMap<>();
Map<String,String> mapInv=new HashMap<>();

for (String key : map.keySet()) 
        mapInv.put(map.get(key), key);

Java 8:
forEach()是一种新的迭代元素的方法。它被定义在Iterable和Stream接口中。
Map<String,String> map=new HashMap<>();
Map<String,String> mapInv=new HashMap<>();

map.forEach((key, value) -> mapInv.put(value, key));

Kotlin :

    val map: Map<String, String> = HashMap()
    val mapInv: MutableMap<String?, String> = HashMap()

    for (key in map.keys) mapInv[map[key]] = key

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接