solvingJ solvingJ - 1 month ago 12
Java Question

Java 8 Stream Collectors for Maps, misleading groupingBy error

I'm pretty confused by the differences in these two methods, but I'm sure I'm doing something wrong.

I have a working example, and a non-working example below. In the working example, I'm assigning the variable tester as a "Map", In the non-working example I'm trying to assign it to Map. In the second example, the error is shown here:

http://pasteboard.co/jscsKyA3S.png

I am failing to see the connection between the types for the tester variable, and the type of the myMap variable.

//Working Example (or at least it compiles)
public String execute(Map<String, String> hostProps) {
Map tester = usages.stream()
.map(usage -> prepareUsageForOutput(usage, rateCard.get()))
.collect(Collectors.groupingBy(myMap -> myMap.get("meterName"))); // This case compiles
}

//Compiler Error, just by adding the Map types on line 13, it breaks line 18
public String execute(Map<String, String> hostProps) {
Map<String, Object> tester = usages.stream()
.map(usage -> prepareUsageForOutput(usage, rateCard.get()))
.collect(Collectors.groupingBy(myMap -> myMap.get("meterName"))); // In this case, the .get() method call can't resolve .get. It doesn't recognize that myMap is a Map.
}


//This is the method that gets called in the above methods
static Map<String, Object> prepareUsageForOutput(Usage usage, RateCard rateCard ){

}


Update 1 - Wrong Collector Method



While Eran posted the explanation of my original issue, it revealed that I should have been using Collectors.toMap instead of Collectors.groupBy, because my goal was to return a map entry for each map returned from "prepare", where the key was a string, and the value was the map returned from "prepare". groupBy will always return a list of results as the map value.

I also removed the filter statements, as they were irrelevant and significantly complicated the example.

This was the final result that met my goal:
Map tester = usages.stream()
.map(usage -> prepareUsageForOutput(usage, rateCard.get()))
.collect(Collectors.toMap(myMap -> (String)myMap.get("meterName"), Function.identity()));

Answer

Since prepareUsageForOutput returns a Map<String, Object>, this means the Collectors.groupingBy() collector operates on a Stream<Map<String, Object>>, which means it creates a Map<Object,List<Map<String, Object>>>, not a Map<String, Object>.

This answer doesn't explain the error your compiler gave, but several times I encountered misleading compilation errors where Streams are involved, so I suggest changing the output type to Map<Object,List<Map<String, Object>>> and see if the error goes away.

Comments