15

I have Map<A, Map<B, C>> and I want to get Map<B, List<C>> from it using Java Streams.

I try to do it as follows:

public <A, B, C> Map<B, List<C>> groupsByInnerKey(Map<A, Map<B, C>> input) {
    return input.values()
            .stream()
            .flatMap(it -> it.entrySet().stream())
            .collect(Collectors.groupingBy(Map.Entry::getKey));
}

What I expect:

  • flatMap gives a Stream of Map.Entry<B, C>
  • collect(Collectors.groupingBy(...)) takes function which is applied to Map.Entry<B, C> and returns B, thus it collects values of C into List<C>.

But it doesn't compile, literally:

Non-static method cannot be referenced from a static context

at Map.Entry::getKey in the last line.

Can someone explain what is wrong or what is the right way to achieve what I want?

Tunaki
  • 132,869
  • 46
  • 340
  • 423
hotkey
  • 140,743
  • 39
  • 371
  • 326
  • Possible duplicate : http://stackoverflow.com/questions/29373026/how-to-get-all-values-from-the-inner-maps-of-a-map-using-a-common-key – Shiladittya Chakraborty Jan 11 '16 at 11:06
  • @ShiladittyaChakraborty, not actually, I'm asking about why the expected way of doing this doesn't work. Also, the task is slightly different from the question you referenced. – hotkey Jan 11 '16 at 11:10

2 Answers2

14

Your Stream is composed of Map.Entry objects but want you want to collect is actually the value of the entry, not the entry itself. With your current code, you would be obtaining a Map<B, List<Map.Entry<B, C>>>.

As such, you are just missing a call to Collectors.mapping. This collector will map the Stream element with the given mapper function and collect that result into the downstream container. In this case, the mapper is Map.Entry::getValue (so returning the value from the map entry) and the downstream collector collects into a List.

public <A, B, C> Map<B, List<C>> groupsByInnerKey(Map<A, Map<B, C>> input) {
    return input.values()
            .stream()
            .flatMap(it -> it.entrySet().stream())
            .collect(Collectors.groupingBy(
                 Map.Entry::getKey,
                 Collectors.mapping(Map.Entry::getValue, Collectors.toList())
            ));
}
Tunaki
  • 132,869
  • 46
  • 340
  • 423
  • 1
    Thanks, it worked. So, I was actually confused by the compiler's error message. It should be saying about types mismatch, not about non-static context. :) – hotkey Jan 11 '16 at 11:18
  • I keep getting `non-static method cannot be referenced from a static context` for `Mep.Entry::getKey` – xjcl Aug 28 '20 at 11:54
  • Solved it, I had to use `AbstractMap.SimpleEntry::getKey`, since I use AbstractMap to create map entries in a stream (does not really make sense since i groupBy but eh) – xjcl Aug 28 '20 at 11:58
8

Your stream pipeline returns a Map<B, List<Map.Entry<B,C>>>, not a Map<B, List<C>>.

To get what a Map<B, List<C>>, you need to add a mapping that would map Map.Entry<B,C> to C :

return input.entrySet()
        .stream()
        .flatMap(it -> it.getValue().entrySet().stream())
        .collect(Collectors.groupingBy(Map.Entry::getKey,Collectors.mapping(Map.Entry::getValue,Collectors.toList())));
Eran
  • 387,369
  • 54
  • 702
  • 768