It’s an interesting question, because it shows that there are many different approaches to achieving the same result. Below I show three different implementations.
Methods predefined in the Collection Framework: Java 8 has added some methods to the collection classes, which are not directly related to the Stream API . Using these methods, you can significantly simplify the implementation of the non-stream implementation:
Collection convert(List multiDataPoints) {
Map result = new HashMap<>();
multiDataPoints.forEach(pt ->
pt.keyToData.forEach((key, value) ->
result.computeIfAbsent(
key, k -> new DataSet(k, new ArrayList<>()))
.dataPoints.add(new DataPoint(pt.timestamp, value))));
return result.values();
}
Stream API with flattening and intermediate data structure: The following implementation is almost identical to the solution provided by Stuart Marks. Contrary to its workaround, the following implementation uses a anonymous internal class as an intermediate data structure.
Collection convert(List multiDataPoints) {
return multiDataPoints.stream()
.flatMap(mdp -> mdp.keyToData.entrySet().stream().map(e ->
new Object() {
String key = e.getKey();
DataPoint dataPoint = new DataPoint(mdp.timestamp, e.getValue());
}))
.collect(
collectingAndThen(
groupingBy(t -> t.key, mapping(t -> t.dataPoint, toList())),
m -> m.entrySet().stream().map(e -> new DataSet(e.getKey(), e.getValue())).collect(toList())));
}
Stream API with Map Blend: Instead of flattening the original data structures, you can also create one Map for each MultiDataPoint , then merge all the maps into a single map with a reduce operation. The code is a little simpler than the solution above:
Collection convert(List multiDataPoints) {
return multiDataPoints.stream()
.map(mdp -> mdp.keyToData.entrySet().stream()
.collect(toMap(e -> e.getKey(), e -> asList(new DataPoint(mdp.timestamp, e.getValue())))))
.reduce(new HashMap<>(), mapMerger())
.entrySet().stream()
.map(e -> new DataSet(e.getKey(), e.getValue()))
.collect(toList());
}
You can find an implementation of map merging within the class Collectors . Unfortunately, it is a bit tricky to access it from the outside. Below is an alternative implementation of map blending :
BinaryOperator