Одним из способов было бы использование новейших JDK12 Collector.teeing
:
Map<String, List<Person>> result = persons.stream()
.collect(Collectors.teeing(
Collectors.groupingBy(Person::getFirstName,
Collectors.toCollection(ArrayList::new)),
Collectors.groupingBy(Person::getLastName),
(byFirst, byLast) -> {
byLast.forEach((last, peopleList) ->
byFirst.computeIfAbsent(last, k -> new ArrayList<>())
.addAll(peopleList));
return byFirst;
}));
Collectors.teeing
, собирающих в два отдельных коллектора, а затем объединяющих результаты в окончательное значение.Из документов:
Возвращает коллектор, составленный из двух нижестоящих коллекторов.Каждый элемент, переданный получающемуся сборщику, обрабатывается обоими нижестоящими сборщиками, затем их результаты объединяются с использованием указанной функции слияния в окончательный результат.
Итак, приведенный выше код собирает карту по имениа также на карту по фамилии, а затем объединяет обе карты в итоговую карту путем итерации карты byLast
и объединения каждой из ее записей в карту byFirst
с помощью Map.computeIfAbsent
метод.Наконец, возвращается карта byFirst
.
Обратите внимание, что я собрал в Map<String, List<Person>>
вместо Map<String, Set<Person>>
, чтобы упростить пример.Если вам действительно нужна карта наборов, вы можете сделать это следующим образом:
Map<String, Set<Person>> result = persons.stream().
.collect(Collectors.teeing(
Collectors.groupingBy(Person::getFirstName,
Collectors.toCollection(LinkedHashSet::new)),
Collectors.groupingBy(Person::getLastName, Collectors.toSet()),
(byFirst, byLast) -> {
byLast.forEach((last, peopleSet) ->
byFirst.computeIfAbsent(last, k -> new LinkedHashSet<>())
.addAll(peopleSet));
return byFirst;
}));
Имейте в виду, что если вам нужно иметь Set<Person>
в качестве значений карт, класс Person
долженреализовать методы hashCode
и equals
последовательно .