Idiomatically creating a multi-value Map from a Stream in Java 8

痞子三分冷 提交于 2019-12-20 23:22:08

问题


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?


回答1:


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());


来源:https://stackoverflow.com/questions/45567546/idiomatically-creating-a-multi-value-map-from-a-stream-in-java-8

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!