Search code examples
javalambdajava-8java-streamcollectors

Collect results of a map operation in a Map using Collectors.toMap or groupingBy


I've got a list of type List<A> and with map operation getting a collective list of type List<B> for all A elements merged in one list.

List<A> listofA = [A1, A2, A3, A4, A5, ...]

List<B> listofB = listofA.stream()
  .map(a -> repo.getListofB(a))
  .flatMap(Collection::stream)
  .collect(Collectors.toList());

without flatmap

List<List<B>> listOflistofB = listofA.stream()
  .map(a -> repo.getListofB(a))
  .collect(Collectors.toList());

I want to collect the results as a map of type Map<A, List<B>> and so far trying with various Collectors.toMap or Collectors.groupingBy options but not able to get the desired result.


Solution

  • You can use the toMap collector with a bounded method reference to get what you need. Also notice that this solution assumes you don't have repeated A instances in your source container. If that precondition holds this solution would give you the desired result. Here's how it looks.

    Map<A, Collection<B>> resultMap = listofA.stream()
        .collect(Collectors.toMap(Function.identity(), repo::getListofB);
    

    If you have duplicate A elements, then you have to use this merge function in addition to what is given above. The merge function deals with key conflicts if any.

    Map<A, Collection<B>> resultMap = listofA.stream()
           .collect(Collectors.toMap(Function.identity(), repo::getListofB, 
                (a, b) -> {
                    a.addAll(b);
                    return a;
            }));
    

    And here's a much more succinct Java9 approach which uses the flatMapping collector to handle repeated A elements.

    Map<A, List<B>> aToBmap = listofA.stream()
            .collect(Collectors.groupingBy(Function.identity(),
                    Collectors.flatMapping(a -> getListofB(a).stream(), 
                            Collectors.toList())));