Collect results of a map operation in a Map using Collectors.toMap or groupingBy
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())));
It would be straight forward,
listofA.stream().collect(toMap(Function.identity(), a -> getListofB(a)));
In this answer, I'm showing what happens if you have repeated A
elements in your List<A> listofA
list.
Actually, if there were duplicates in listofA
, the following code would throw an IllegalStateException
:
Map<A, Collection<B>> resultMap = listofA.stream()
.collect(Collectors.toMap(
Function.identity(),
repo::getListofB);
The exception might be thrown because Collectors.toMap
doesn't know how to merge values when there is a collision in the keys (i.e. when the key mapper function returns duplicates, as it would be the case for Function.identity()
if there were repeated elements in the listofA
list).
This is clearly stated in the docs:
If the mapped keys contains duplicates (according to
Object.equals(Object)
), anIllegalStateException
is thrown when the collection operation is performed. If the mapped keys may have duplicates, usetoMap(Function, Function, BinaryOperator
) instead.
The docs also give us the solution: in case there are repeated elements, we need to provide a way to merge values. Here's one such way:
Map<A, Collection<B>> resultMap = listofA.stream()
.collect(Collectors.toMap(
Function.identity(),
a -> new ArrayList<>(repo.getListofB(a)),
(left, right) -> {
left.addAll(right);
return left;
});
This uses the overloaded version of Collectors.toMap
that accepts a merge function as its third argument. Within the merge function, Collection.addAll
is being used to add the B
elements of each repeated A
element into a unqiue list for each A
.
In the value mapper function, a new ArrayList
is created, so that the original List<B>
of each A
is not mutated. Also, as we're creating an Arraylist
, we know in advance that it can be mutated (i.e. we can add elements to it later, in case there are duplicates in listofA
).