在Java 8中将键值为Map<Key, List<Value>>的Map反转为Map<Value, Key>

7
我是一位有用的助手,可以为您翻译文本。
我有一个按键分组值的映射 Map<String,List<Integer>>,我想要将其倒转以便将每个值映射到相应的键
例如:我想转换下面的代码
Map<String, List<Integer>> mapOfIntList = new HashMap<String, List<Integer>>();

mapOfIntList.put("UNIT", Arrays.asList(1, 2, 3, 8, 7, 0, 8, 6));
mapOfIntList.put("TEN", Arrays.asList(24, 90, 63, 87));
mapOfIntList.put("HUNDRED", Arrays.asList(645, 457, 306, 762));
mapOfIntList.put("THOUSAND", Arrays.asList(1234, 3456, 5340, 9876));

将其转换为另一个Map(Integer, String) ,其中我可以找到: (1,"UNIT"),(2,"UNIT") ...(24,"TEN"),(90,"TEN")...(645,"HUNDRED")...(3456,"THOUSAND")...
4个回答

14

您可以使用

Map<Integer, String> mapNumberToType = mapOfIntList.entrySet().stream()
    .collect(HashMap::new, (m,e)->e.getValue().forEach(v->m.put(v,e.getKey())), Map::putAll);

在传递给collect函数的第二个函数中(累加器),您可能会注意到与此答案中基于forEach的代码相似之处。对于顺序执行,它们基本上是相同的,但这个Stream解决方案支持并行处理。这就是为什么它需要另外两个函数来支持创建本地容器和合并它们。

另请参阅文档中的可变归约部分。


运行得很好!...谢谢! - Shessuky
@Holger,需要哪些更改来支持并行处理? - Hadi J
2
@HadiJ 没有。这就是重点。两个嵌套的 forEach 更简单,而这个基于 collect 的解决方案支持并行处理。 - Holger
如果我的值不是唯一的怎么办?mapOfIntList.put("UNIT", Arrays.asList(1, 2)); mapOfIntList.put("TEN", Arrays.asList(2, 90)); 在上面的代码中,我将得到 2=TEN 而不是 2=[UNIT, TEN]。这个脚本能否被修改以处理非唯一的值? - Erik Steiner

6

或者使用两个嵌套的forEach

mapOfIntList.forEach((key, value) ->
            value.forEach(v -> {
                mapNumberToType.put(v, key);
            })
 );

在一行代码中,正如@nullpointer评论的那样
mapOfIntList.forEach((key, value) -> value.forEach(v -> mapNumberToType.put(v, key)));

4
我找到了一个解决方案:
Map<Integer, String> mapNumberToType = mapOfIntList
    .entrySet()
    .stream()
    .flatMap(
            entry -> entry.getValue().stream()
                    .map(number -> Pair.of(number, entry.getKey()))
                    .collect(Collectors.toList()).stream())
    .collect(
            Collectors.toMap(Pair::getLeft,
                    Pair::getRight, (a, b) -> {
                        return a;
                    }));

System.out.println("Number/Type correspondance : " + mapNumberToType);

希望这能帮到遇到同样问题的人!

2
最后的收集可以简化为:.collect(Collectors.toMap(Pair::getLeft, Pair::getRight)); - Naman
2
@nullpointer 好的,只有在您保证没有重复值的情况下才可以这样做。另外,在flatMap()中使用.collect(Collectors.toList()).stream()是不必要的。 - Didier L
@DidierL在这种情况下,重复的正确行为是什么? - Holger
@DidierL 我第一次使用第二个 map 时没有进行收集和流式处理,但是它不起作用,编译器在此阶段之后无法检测到对象类型。 - Shessuky
@Holger 这取决于提问者的需求,他也编写了这个答案。然而,这种简化并不等同于原始代码。 - Didier L
@Shessuky,使用Aomine的答案(使用SimpleEntry而不是Pair)似乎可以工作。 - Didier L

4
这可以简化为:
 source.entrySet()
       .stream()
       .flatMap(e -> e.getValue().stream().map(s -> new AbstractMap.SimpleEntry<>(s, e.getKey())))
       .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (l, r) -> l));

刚刚测试了一下,遇到了一个问题:toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue, (<no type> l, <no type> r) -> {}) 方法在 DateParser 类型中未定义。我漏掉了什么吗? - Shessuky
2
你从哪里得到了 DateParser?我已经根据你在帖子中提供的数据进行了测试。请查看 ideone 获取一个可工作的示例。 - Ousmane D.
我已经在ideone上检查了代码,然后创建了一个与该代码相同的类,但是在所有流中都出现了错误,错误信息为:类型不匹配:无法从Map<Object,Object>转换为Map<Integer,String>,我会深入研究这个问题...非常感谢! - Shessuky

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