web-dev-qa-db-ja.com

Java 8のストリームから複数値のマップを慣例的に作成する

Java 8のストリームAPIを使用して、複数値_Map<K,Collection<V>>_をエレガントに初期化してデータを設定する方法はありますか?

Collectors.toMap(..) 関数を使用して単一値_Map<K, V>_を作成できることを知っています。

_Stream<Person> persons = fetchPersons();
Map<String, Person> personsByName = persons.collect(Collectors.toMap(Person::getName, Function.identity()));
_

残念ながら、この方法は、人の名前など、一意でない可能性のあるキーにはうまく機能しません。

一方、 Map.compute(K, BiFunction<? super K,? super V,? extends V>>) を使用して複数値_Map<K, Collection<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;
}));
_

これを行うためのこれ以上の簡潔な方法はありませんか? 1つのステートメントでマップを初期化して入力することによって?

10
errantlinguist

forEachを使用する場合は、computeIfAbsentの代わりに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を使用します。

Map<String, Set<Person>> personsByName =
    persons.collect(Collectors.groupingBy(Person::getName, Collectors.toSet());
19
Holger