Vennik
Vennik

Reputation: 565

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

Upvotes: 3

Views: 366

Answers (1)

Misha
Misha

Reputation: 28133

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

Upvotes: 3

Related Questions