Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 Lambda (grouping and reducing in a single step)

Say I have a List of Pair objects,

List<Pair<A,B>> listOfPairs = //some list of pairs ; 

I would like to group this list into a Map<A,Set<B>>.

Currently, I can do this in two steps. The first step groups by A, and returns a Map<A,Set<Pair<A,B>> as follows:

   Map<A,Set<Pair<A,B>> intermediateStep =   listOfPairs.stream().collect(Collectors.groupingBy((Pair::getLeft), Collectors.toSet()));

I then stream the entryset of the map above and collect them into the desired end result, mainly by mapping each Pair object to its B value, and collecting them to a set:

 Map<A, Set<B>> finalResult= intermediateStep.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue().stream().map(Pair::getRight).collect(Collectors.toSet())));

Is there a better way to achieve the desired result, perhaps in one step? In other words, the intermediate step mentioned above groups by A, but the right side of the grouping returns the entire Pair object. I'd like to group by A and point A to the set of its associated Bs in one step.

(I know that just because I could do it in one step, doesn't mean I should, because readability might suffer, but I'm curious.)

Thanks!

like image 261
mancini0 Avatar asked Jan 26 '18 16:01

mancini0


2 Answers

You can simplify the code by passing a mapping collector into the groupingBy collector like this:

Map<A, Set<B>> collect = 
       listOfPairs.stream()
                  .collect(Collectors.groupingBy(Pair::getLeft, 
                                Collectors.mapping(Pair::getRight,
                                       Collectors.toSet())));
like image 71
Ousmane D. Avatar answered Nov 08 '22 06:11

Ousmane D.


You can do it using Map.computeIfAbsent:

Map<A, Set<B>> finalResult = new HashMap<>();
listOfPairs.forEach(pair -> finalResult.computeIfAbsent(
        pair.getLeft(), 
        k -> new HashSet<>())
    .add(pair.getRight()));

If you want to preserve insertion order you can use LinkedHashMap and LinkedHashSet instead of HashMap and HashSet, respectively.

like image 2
fps Avatar answered Nov 08 '22 04:11

fps