Vennik Vennik - 5 months ago 21
Java Question

How to remove a collect into a new stream from the middle of a java 8 stream?

I'm working on a Java 8 stream. And I need group by 2 keys in a map. And then put these keys with their value into a new function.

Is there a way to skip the

Collector
and reading it out again?

graphs.stream()
.map(AbstractBaseGraph::edgeSet)
.flatMap(Collection::stream)
.collect(Collectors.groupingBy(
graph::getEdgeSource,
Collectors.groupingBy(
graph::getEdgeTarget,
Collectors.counting()
)
))
.entrySet().stream()
.forEach(startEntry ->
startEntry.getValue().entrySet().stream()
.forEach(endEntry ->
graph.setEdgeWeight(
graph.addEdge(startEntry.getKey(), endEntry.getKey()),
endEntry.getValue() / strains
)));

Answer

No, you have to have some sort of an intermediate data structure to accumulate counts. Depending on how your graph and edge classes are written, you could try to accumulate counts directly into the graph, but that would be less readable and more brittle.

Note that you can iterate over the intermediate map more concisely by using Map#forEach:

.forEach((source, targetToCount) -> 
    targetToCount.forEach((target, count) -> 
        graph.setEdgeWeight(graph.addEdge(source, target), count/strains)
    )
);

You can also collect the counts into Map<List<Node>, Long> instead of Map<Node,Map<Node,Long>> if you dislike the map-of-maps approach:

graphs.stream()
    .map(AbstractBaseGraph::edgeSet)
    .flatMap(Collection::stream)
    .collect(groupingBy(
            edge -> Arrays.asList(
                graph.getEdgeSource(edge), 
                graph.getEdgeTarget(edge)
            ),
            counting()
    ))
    .forEach((nodes, count) -> 
        graph.setEdgeWeight(graph.addEdge(nodes.get(0), nodes.get(1)), count/strains)
    );
Comments