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 use computeIfAbsent instead of compute:
Map<String, Set<Person>> personsByName = new HashMap<>();
persons.forEach(person ->
    personsByName.computeIfAbsent(person.getName(), key -> new HashSet<>()).add(person));
However, when using the Stream API, it’s preferable to use collect. In this case, use groupingBy instead of toMap:
Map<String, Set<Person>> personsByName =
    persons.collect(Collectors.groupingBy(Person::getName, Collectors.toSet());
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With