5

I am new to Java8. A problem that I want to solve is to convert Map> to Map using Stream. For example:

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

Let's assume that there is no duplicated value in List. How to do it in java 8 stream in an elegant way?

Cheers, Wei

user140547
  • 7,750
  • 3
  • 28
  • 80
liuwei7923
  • 53
  • 3

4 Answers4

4

If you want a solution without forEach() you can do:

    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));

For each Entry like "3"-> [3,4,5,6] a Stream of Entries like 3->3,4->3,5->3,6->3 is created, using flatMap it can be flattened to one stream, then Collectors.toMap() creates the final Map.

user140547
  • 7,750
  • 3
  • 28
  • 80
  • I tried the exact same approach. And eclipse shows me a compile error, which is "The type Map.Entry does not define getKey(Object) that is applicable here". After reading your answer, I researched a bit on google and found out that it is a know issue with Luna, detail in here: http://stackoverflow.com/questions/33091946/java-8-stream-flatmap-and-group-by-code-compiler-error. After upgrade to Mars, everything just worked. Thanks! – liuwei7923 Sep 18 '16 at 20:49
3

Assuming

    Map<String, List<Integer>> map = new HashMap<>();
    Map<Integer,String>        pam = new HashMap<>();

This will do what you want

    map.entrySet().stream().forEach(e -> e.getValue().stream().forEach(v -> pam.put(v, e.getKey())));

This takes advantage of the fact that Set<E> implements stream() from Collection. The rest is just plugging things into the right place.

Or, as suggested by @user140547 (thank you), an even simpler solution

    map.forEach((k,v) -> v.forEach(vv -> pam.put(vv, k)));
Jim Garrison
  • 85,615
  • 20
  • 155
  • 190
  • Among all the above approach, which one do you recommend. – liuwei7923 Sep 18 '16 at 20:53
  • @liuwei7923 The second one. It is the most succinct and idiomatic. Also, you should upgrade to the latest Eclipse (Neon). It is quite possible older versions have bugs in compiling lambda expressions. You are TWO major releases behind. – Jim Garrison Sep 19 '16 at 01:18
3

It's really reasy using the free StreamEx library written by me:

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

Here EntryStream.of(map) creates a stream of map entries which is extended by additional operations; invert() swaps keys and values, flatMapKeys() flattens keys leaving values unchanged and toMap() collects the resulting entries back to map.

Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
0

As pointed by @JimGarrison Using Map.forEach it is much easier

input.forEach( (key,value)-> value.forEach(vvalue -> output.put(vvalue, key)));
ravthiru
  • 8,878
  • 2
  • 43
  • 52