Perform FlatMap operation with collectors

I would like to understand the way flatMap executed when using Collector s. Here is an example.

Scenario:

I have the following interfaces:

 interface Ic { //empty } interface Ib { Stream<Ic> getCs(); } interface Ia { String getName(); Stream<Ib> getBs(); } 

And I am trying to implement the following method:

 Map<String, Long> total_of_C_per_A (Stream<Ia> streamOfA) { return streamOfA.collect(groupBy(Ia::getName, ???)); } 

The classification function is quite simple, my problem is with the downstream collector. I need to count the number of β€œC” associated with β€œA”.

What I tried:

If I just wanted to return the account without creating a card, I would do:

 streamOfA .flatMap(Ia::getBs) .flatMap(Ib::getCs) .count(); 

But the Collectors class allows me to perform display operations. What else can I do?

Thanks.

+5
source share
2 answers

This answer indicates that you are already in the right direction, but there is no need to embed multiple mapping collectors, as you can simply write these functions into a single lambda expression. Given that the summingLong collector expects a function that evaluates to long , you can simply pass this function to the collector without any mapping collector:

 Map<String, Long> total_of_C_per_A (Stream<Ia> streamOfA) { return streamOfA.collect(groupingBy( Ia::getName, summingLong(ia -> ia.getBs().flatMap(Ib::getCs).count()))); } 

This also has the advantage that long values ​​do not fit into long instances.

There is also an alternative to flatMap :

 Map<String, Long> total_of_C_per_A (Stream<Ia> streamOfA) { return streamOfA.collect(groupingBy( Ia::getName, summingLong(ia -> ia.getBs().mapToLong(ib -> ib.getCs().count()).sum()))); } 
+6
source

the documentation describes Collectors # mapping as:

Adapts a Collector element that accepts type U elements to one receive element of type T , applying a matching function to each input element before accumulating.

The mapping() collectors are most useful when used in multilevel reduction, for example, below groupingBy or partitioningBy .

which means that you can create any possible Collector as you can.

 import static java.util.stream.Collectors.*; Map<String, Long> total_of_C_per_A(Stream<Ia> streamOfA) { return streamOfA.collect(groupingBy( Ia::getName, mapping( Ia::getBs, mapping( it -> it.flatMap(Ib::getCs), // reduce() does boxing & unboxing ---v mapping(Stream::count, reducing(0L,Long::sum)) ) ) )); } 

OR , using # summingLong instead of the collector .

 Map<String, Long> total_of_C_per_A(Stream<Ia> streamOfA) { return streamOfA.collect(groupingBy( Ia::getName, mapping( Ia::getBs, mapping( it -> it.flatMap(Ib::getCs), // summingLong() does boxing ---v mapping(Stream::count, summingLong(Long::longValue)) // Long::longValue does unboxing operation ---^ ) ) )); } 

thanks for @Holger pointing out the potential problem of the above code, instead you can just use summingLong(Stream::count) . in this approach, there is no need to box Stream#count , which returns long to long . and Long::longValue unboxing a long to long .

 Map<String, Long> total_of_C_per_A(Stream<Ia> streamOfA) { return streamOfA.collect(groupingBy( Ia::getName, mapping( Ia::getBs, // summingLong() doesn't any boxing ---v mapping(it -> it.flatMap(Ib::getCs), summingLong(Stream::count)) ) )); } 
+5
source

Source: https://habr.com/ru/post/1269211/


All Articles