在Java 8中从流中以惯用方式创建多值Map [英] Idiomatically creating a multi-value Map from a Stream in Java 8
问题描述
有没有办法使用Java 8的流API优雅地初始化和填充多值 Map< K,Collection< V>>
?
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.
另一方面,可以填充多值 Map< K,Collection< V>>
使用 Map.compute(K,BiFunction<?super K,?super V,?extends V>>)
:
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?
推荐答案
如果你使用 forEach
,使用 computeIfAbsent
而不是 compute
更简单:
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));
但是,使用Stream API时,最好使用 collect
。在这种情况下,使用 groupingBy
而不是 toMap
:
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());
这篇关于在Java 8中从流中以惯用方式创建多值Map的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!