Вопрос или проблема
Я пытаюсь сгруппировать по (отобразить), а затем преобразовать список значений в другой список.
У меня есть список DistrictDocuments:
List<DistrictDocument> docs = new ArrayList<>();
Затем я итерирую по нему и группирую по городу:
Map<String, List<DistrictDocument>> collect = docs.stream()
.collect(Collectors.groupingBy(DistrictDocument::getCity));
У меня также есть метод, который принимает DistrictDocument и создает из него Slugable:
private Fizz createFizz(DistrictDocument doc) {
return new Fizz().name(doc.getName()).fizz(doc.getFizz());
}
Есть ли способ вставить этот метод в мой поток выше, чтобы получить Map<String, List<Fizz>>
?
Я пытался добавить второй аргумент к groupingBy, но не смог найти правильный способ и постоянно получал ошибки компиляции.
Редактирование:
Что если мой createFizz возвращает List<Fizz>
? Есть ли возможность сгладить этот список в Collectors.mapping, потому что я все равно хочу получить Map<String, List<Fizz>>
вместо Map<String, List<List<Fizz>>>
Вам необходимо связать коллектор Collectors.mapping()
с коллектором Collectors.groupingBy()
:
Map<String, List<Fizz>> collect =
docs.stream()
.collect(Collectors.groupingBy(DistrictDocument::getCity,
Collectors.mapping(d->createFizz(d),Collectors.toList())));
Если createFizz(d)
возвращает List<Fizz
, вы можете сгладить его, используя Collectors.flatMapping
из Java 9:
Map<String, List<Fizz>> collect =
docs.stream()
.collect(Collectors.groupingBy(DistrictDocument::getCity,
Collectors.flatMapping(d->createFizz(d).stream(),Collectors.toList())));
Если вы не можете использовать Java 9, возможно, использование Collectors.toMap
поможет:
Map<String, List<Fizz>> collect =
docs.stream()
.collect(Collectors.toMap(DistrictDocument::getCity,
d->createFizz(d),
(l1,l2)->{l1.addAll(l2);return l1;}));
В случае, если вы хотите сделать двойную вложенную группировку:
Допустим, у вас есть коллекция объектов EducationData
, которые содержат название школы, имя учителя и имя студента. Но вы хотите вложенную карту, которая выглядит так:
Что у вас есть
class EducationData {
String school;
String teacher;
String student;
// геттеры и сеттеры ...
}
Что вы хотите
Map<String, Map<String, List<String>>> desiredMapOfMpas ...
// которая будет выглядеть так :
"East High School" : {
"Ms. Jackson" : ["Derek Shepherd", "Meredith Grey", ...],
"Mr. Teresa" : ["Eleanor Shellstrop", "Jason Mendoza", ...],
....
}
Как к этому прийти
import static java.util.stream.Collectors.*;
public doubleNestedGroup(List<EducationData> educations) {
Map<String, Map<String, List<String>>> nestedMap = educations.stream()
.collect(
groupingBy(
EducationData::getSchool,
groupingBy(
EducationData::getTeacher,
mapping(
EducationData::getStudent,
toList()
)
)
)
);
}
Ответ или решение
Группировка и маппинг значений в Java Stream API
В данной статье мы рассмотрим, как выполнять группировку и преобразование значений в коллекциях при помощи Java Stream API. Мы подробно разберем, как создать карту (Map
), группируя объекты по определенному полю, а затем преобразовать сгруппированные значения в другой тип.
Исходные данные
Предположим, у вас есть список документов районов (DistrictDocument
), который вы хотите сгруппировать по городу и затем преобразовать в другой тип данных. Пример определения класса может выглядеть следующим образом:
class DistrictDocument {
private String city;
private String name;
private String fizz;
// Геттеры и сеттеры...
}
У вас также есть метод, который принимает DistrictDocument
и создает объект Fizz
:
private Fizz createFizz(DistrictDocument doc) {
return new Fizz().name(doc.getName()).fizz(doc.getFizz());
}
Группировка и преобразование
Для того чтобы получить Map<String, List<Fizz>>
, вам необходимо использовать метод Collectors.groupingBy()
в сочетании с Collectors.mapping()
. Это выглядит следующим образом:
Map<String, List<Fizz>> collect = docs.stream()
.collect(Collectors.groupingBy(
DistrictDocument::getCity,
Collectors.mapping(this::createFizz, Collectors.toList())
));
В данной конструкции:
DistrictDocument::getCity
— это функция, по которой происходит группировка.Collectors.mapping(this::createFizz, Collectors.toList())
— это маппинг, который преобразует каждыйDistrictDocument
вFizz
.
Обработка списка значений
Если ваш метод createFizz
возвращает список объектов Fizz
, вам потребуется использовать Collectors.flatMapping()
, чтобы избежать получения вложенной структуры Map<String, List<List<Fizz>>>
. Пример кода будет таким:
Map<String, List<Fizz>> collect = docs.stream()
.collect(Collectors.groupingBy(
DistrictDocument::getCity,
Collectors.flatMapping(d -> createFizz(d).stream(), Collectors.toList())
));
Здесь flatMapping
позволяет "разгладить" список и включить все значения в один список, в результате чего мы получаем желаемую структуру.
Альтернативное решение без Java 9
Если вы используете версию Java ниже 9, можно воспользоваться методом Collectors.toMap()
в сочетании с обработкой конфликтующих значений:
Map<String, List<Fizz>> collect = docs.stream()
.collect(Collectors.toMap(
DistrictDocument::getCity,
d -> new ArrayList<>(Collections.singletonList(createFizz(d))),
(l1, l2) -> { l1.addAll(l2); return l1; }
));
Здесь l1.addAll(l2)
объединяет два списка в случае конфликтов.
Двойная группировка
Если вам необходимо создать вложенную структуру, например, Map<String, Map<String, List<String>>>
, вы можете сделать это следующим образом:
class EducationData {
private String school;
private String teacher;
private String student;
// Геттеры и сеттеры...
}
public Map<String, Map<String, List<String>>> doubleNestedGroup(List<EducationData> educations) {
return educations.stream()
.collect(Collectors.groupingBy(
EducationData::getSchool,
Collectors.groupingBy(
EducationData::getTeacher,
Collectors.mapping(
EducationData::getStudent,
Collectors.toList()
)
)
));
}
Таким образом, полученная карта будет соответствовать формату, который вы ожидаете, группируя учеников по учителям и школам.
Заключение
Использование Java Stream API для группировки и маппинга значений позволяет легко преобразовывать ваши данные в необходимый формат. Метод Collectors.groupingBy()
в комбинации с Collectors.mapping()
и Collectors.flatMapping()
предоставляет мощные инструменты для работы с коллекциями. Надеюсь, эта информация была полезной и поможет вам в ваших проектах.