Java8: 如何使用Stream将Map<X, List<Y>>转换为Map<Y,X>?

5

我是Java8的新手,我的一个问题是如何使用Stream将Map<K, List<V>>转换为Map<V, List<K>>。例如:

input: {A => [B, C, D], E => [F]}
output: {B => A, C => A, D => A, F => E}

假设列表中没有重复的值。如何在Java 8流中以优雅的方式实现?
祝好,
Wei

流是必须的吗? - ChiefTwoPencils
你尝试过解决这个问题吗?到目前为止,你都尝试了什么? - Jim Garrison
@Jim,我尝试了与user149547相同的方法。但是,我的eclipse Luna拒绝执行正确的类型引用,这让我觉得我错了。 - liuwei7923
@ChiefTwoPencils:你还有什么建议吗?我只是想找到一个简单易读的方式来完成这项工作。 - liuwei7923
我打算给出Jim Garrison在第二部分中提供的内容,因为在我看来这是最干净的。 - ChiefTwoPencils
谢谢。我会选择最干净的方式。感谢大家的帮助! - liuwei7923
4个回答

4

如果你想不使用forEach()来解决问题,你可以这样做:

    Map<Integer, String> pam = 
            map.entrySet().stream()
            .flatMap(x -> x.getValue().stream().map(v -> new AbstractMap.SimpleEntry<>(v, x.getKey())))
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

对于每个入口,如"3"-> [3,4,5,6],将创建一组条目流,如3->3,4->3,5->3,6->3,使用flatMap可将其展平为一个流,然后Collectors.toMap()创建最终的Map


我尝试了完全相同的方法。但是Eclipse显示了一个编译错误,即“类型Map.Entry没有定义适用于此处的getKey(Object)”。在阅读了您的答案之后,我在Google上进行了一些研究,并发现这是Luna的已知问题,在这里有详细信息:https://dev59.com/WpDea4cB1Zd3GeqPWAYw。升级到Mars后,一切正常了。谢谢! - liuwei7923

3
假设:
    Map<String, List<Integer>> map = new HashMap<>();
    Map<Integer,String>        pam = new HashMap<>();

这将实现您想要的功能。
    map.entrySet().stream().forEach(e -> e.getValue().stream().forEach(v -> pam.put(v, e.getKey())));

这利用了 Set<E> 实现自 Collectionstream() 接口。其余部分只是将东西插入到正确的位置。另外,正如 @user140547 (谢谢)建议的那样,还有一个更简单的解决方案。
    map.forEach((k,v) -> v.forEach(vv -> pam.put(vv, k)));

在所有上述方法中,你推荐哪一个? - liuwei7923
@liuwei7923 第二个。它最为简洁和惯用。同时,建议你升级到最新的Eclipse(Neon)。老版本可能会在编译Lambda表达式时出现错误。你距离最新版本已经有两个主要版本的差距了。 - Jim Garrison

3

使用由我编写的免费StreamEx库非常简单:

EntryStream.of(map).invert().flatMapKeys(Collection::stream).toMap();

这里的EntryStream.of(map)创建了一个map条目流,可以通过其他操作进行扩展;invert()交换键和值,flatMapKeys()将键扁平化,保持值不变,toMap()将结果条目收集回map。


0

正如@JimGarrison所指出的那样,使用Map.forEach会更容易

input.forEach( (key,value)-> value.forEach(vvalue -> output.put(vvalue, key)));

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