Is there any way to elegantly initialize and populate a multi-value Map<K,Collection<V>>
using Java 8's stream API?
I know it's possible to create a single-value Map<K, V>
using the Collectors.toMap(..)
functionalities:
Stream<Person> persons = fetchPersons();
Map<String, Person> personsByName = persons.collect(Collectors.toMap(Person::getName, Function.identity()));
Unfortunately, that method won't work well for possibly non-unique keys such as a person's name.
On the other hand, it's possible to populate a multi-value Map<K, Collection<V>>
using Map.compute(K, BiFunction<? super K,? super V,? extends V>>)
:
Stream<Person> persons = fetchPersons();
Map<String, Set<Person>> personsByName = new HashMap<>();
persons.forEach(person -> personsByName.compute(person.getName(), (name, oldValue) -> {
Set<Person> result = (oldValue== null) ? new HashSet<>() : oldValue;
result.add(person);
return result;
}));
Is there no more concise way of doing this, e.g. by initializing and populating the map in one statement?
If you use
forEach
, it’s much simpler to usecomputeIfAbsent
instead ofcompute
:However, when using the Stream API, it’s preferable to use
collect
. In this case, usegroupingBy
instead oftoMap
: