Создание потока List of List (вложенный список) с использованием forEach, Java 8
class EntityCompositeId {
private Long firstId;
private Long secondId;
// getter & setter...
}
class EntityComposite {
private EntityCompositeId id;
private String first;
private String second;
// getter & setter...
}
List<EntityComposite> listEntityComposite = ....
Supose this content
1, 1, "firstA", "secondBirdOne"
1, 2, "firstA", "secondBirdTwo"
1, 3, "firstA", "secondBirdThree"
2, 1, "firstB", "secondCatOne"
2, 2, "firstB", "secondCatTwo"
2, 3, "firstB", "secondCatThree"
3, 1, "firstC", "secondDogOne"
3, 2, "firstC", "secondDogTwo"
3, 3, "firstC", "secondDogThree"
Map<Long, List<String>> listOfLists = new HashMap<>();
Теперь с помощью потока я хочу заполнить как:
1 -> {"secondBirdOne", "secondBirdTwo", "secondBirdThree"}
2 -> {"secondCatOne", "secondCatTwo", "secondCatThree"}
3 -> {"secondDogOne", "secondDogTwo", "secondDogThree"}
Мой незаконченный (вот в чем вопрос) код:
listEntityComposite.stream()forEach(entityComposite {
// How create a list according entityComposite.getId.getFirstId()?
listOfLists.put(entityComposite.getId.getFirstId(), .... )
});
3 ответа
Есть несколько разных подходов, в которых вы можете выполнить поставленную задачу.
forEach + computeIfAbsent
Map<Long, List<String>> map = new HashMap<>();
listEntityComposite.forEach(e -> map.computeIfAbsent(e.getId().getFirstId(),
k -> new ArrayList<>()).add(e.getSecond()));
- перечисляет элементы в
listEntityComposite
с помощьюforEach
- для каждого элемента использует
computeIfAbsent
вычислить ключ (т.е.firstId
) и стоимость (то естьList<String>
)
groupingBy + отображение
Другой подход заключается в применении groupingBy
с mapping
нижний коллектор:
Map<Long, List<String>> resultSet = listEntityComposite.stream()
.collect(groupingBy(e -> e.getId().getFirstId(),
mapping(EntityComposite::getSecond, toList())));
- группирует исходные элементы по функции классификации
e.getId().getFirstId()
а затем применяетmapping
нижестоящий коллектор для дальнейшего уточнения нашего запроса.
forEach + слияние
listEntityComposite.forEach(e -> map.merge(e.getId().getFirstId(),
new ArrayList<>(singletonList(e.getSecond())),
(l, r) -> {l.addAll(r); return l;}));
перечисляет элементы в
listEntityComposite
с помощьюforEach
для каждого элемента использует
merge
вычислить ключ (т.е.firstId
) и стоимость (то естьList<String>
)
на карту
listEntityComposite.stream()
.collect(toMap(e -> e.getId().getFirstId(),
v -> new ArrayList<>(singletonList(v.getSecond())),
(l, r) -> {l.addAll(r); return l;}));
- применяет
keyMapper
функцияe -> e.getId().getFirstId()
извлечь ключи карты. - применяет
valueMapper
функцияv -> new ArrayList<>(singletonList(v.getSecond()))
извлечь значения карты. - применяет
merge
функция(l, r) -> {l.addAll(r); return l;}
разрешать ключевые коллизии.
В заключение, forEach
+ computeIfAbsent
подход и groupingBy
+ mapping
Подход - это то, что вам следует отдать предпочтение в этом конкретном случае, поскольку они более идиоматичны.
collect
является более подходящей терминальной операцией для генерации выхода Map
чем forEach
,
Ты можешь использовать collect()
с Collectors.groupingBy
:
Map<Long, List<String>> listOfLists =
listEntityComposite.stream()
.collect(Collectors.groupingBy(e -> e.getId().getFirstId(),
Collectors.mapping(EntityComposite::getSecond,
Collectors.toList());
Collectors.groupingBy
с одним аргументом (просто e -> e.getId().getFirstId()
) будет генерировать Map<Long,List<EntityComposite>>
,
Привязать к нему Collectors.mapping()
карты каждый EntityComposite
экземпляр к соответствующему getSecond()
String
, как требуется.
Я бы посоветовал взглянуть на MultiMap от Guava, который облегчает работу с вашим вариантом использования (предоставляя множество оптимизаций и дополнительную функциональность, которую вы, возможно, захотите получить позже в будущем)
Редактировать: пример того, почему Multimap имеет больше смысла, чем, скажем, использование подхода computeIfAbsent: 1. у каждого ключа есть список определенного размера, что если вы захотите получить "общий" размер в будущем? вам нужно было бы создать некоторую логику для достижения этой цели с хорошей производительностью (или использовать метод, который использует O(ключи)) 2. в данный момент вы только помещаете вещи в карту, но что произойдет, если вы хотите удалить вещи из карта в будущем? Вам нужно будет написать некоторый шаблонный код (который легко ошибиться), чтобы убедиться, что удаление значений не вызывает утечку памяти
Есть и другие преимущества использования мультикарты, но это всего лишь два легко объяснимых преимущества.
Редактировать 2: пример с использованием вашего ввода:
import java.util.Arrays;
import java.util.List;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
public class Example {
public static class EntityCompositeId {
@Override
public String toString() {
return "EntityCompositeId [firstId=" + firstId + ", secondId=" + secondId + "]";
}
public EntityCompositeId(Long firstId, Long secondId) {
super();
this.firstId = firstId;
this.secondId = secondId;
}
private Long firstId;
public Long getFirstId() {
return firstId;
}
private Long secondId;
}
public static class EntityComposite {
@Override
public String toString() {
return "EntityComposite [id=" + id + ", first=" + first + ", second=" + second + "]";
}
public EntityComposite(EntityCompositeId id, String first, String second) {
super();
this.id = id;
this.first = first;
this.second = second;
}
private EntityCompositeId id;
public EntityCompositeId getId() {
return id;
}
private String first;
private String second;
}
public static void main(String[] args) {
List<EntityComposite> listEntityComposite = Arrays.asList(
new EntityComposite(new EntityCompositeId(1l, 1l), "firstA", "secondBirdOne"),
new EntityComposite(new EntityCompositeId(1l, 2l), "firstA", "secondBirdTwo"),
new EntityComposite(new EntityCompositeId(1l, 3l), "firstA", "secondBirdThree"),
new EntityComposite(new EntityCompositeId(2l, 1l), "firstB", "secondCatOne"),
new EntityComposite(new EntityCompositeId(2l, 2l), "firstB", "secondCatTwo"),
new EntityComposite(new EntityCompositeId(2l, 3l), "firstB", "secondCatThree"),
new EntityComposite(new EntityCompositeId(3l, 1l), "firstC", "secondDogOne"),
new EntityComposite(new EntityCompositeId(3l, 2l), "firstC", "secondDogTwo"),
new EntityComposite(new EntityCompositeId(3l, 3l), "firstC", "secondDogThree"));
ListMultimap<Long, EntityComposite> map = ArrayListMultimap.create();
listEntityComposite.forEach(entityComposite -> map.put(entityComposite.getId().getFirstId(), entityComposite));
map.keySet().forEach(key -> System.out.println(map.get(key)));
}
}
Получает следующий вывод:
[EntityComposite [id=EntityCompositeId [firstId=1, secondId=1], first=firstA, second=secondBirdOne], EntityComposite [id=EntityCompositeId [firstId=1, secondId=2], first=firstA, second=secondBirdTwo], EntityComposite [id=EntityCompositeId [firstId=1, secondId=3], first=firstA, second=secondBirdThree]]
[EntityComposite [id=EntityCompositeId [firstId=2, secondId=1], first=firstB, second=secondCatOne], EntityComposite [id=EntityCompositeId [firstId=2, secondId=2], first=firstB, second=secondCatTwo], EntityComposite [id=EntityCompositeId [firstId=2, secondId=3], first=firstB, second=secondCatThree]]
[EntityComposite [id=EntityCompositeId [firstId=3, secondId=1], first=firstC, second=secondDogOne], EntityComposite [id=EntityCompositeId [firstId=3, secondId=2], first=firstC, second=secondDogTwo], EntityComposite [id=EntityCompositeId [firstId=3, secondId=3], first=firstC, second=secondDogThree]]