1Z10 :
I have a class with the following fields:
public class Item{
private String name;
private Long category;
private Set<Long> containers;
}
What I need to do is turn a
List<Item> items
into a
Map<Long/*categoryID*/, Set<Long/*Containers*/>>
using the Java 8 Stream API.
Right now I'm able to get the same result using Itarable
and some if
, like this:
List<Item> items = getItems();
Iterator<Item> itemsIterator = items.iterator();
Map<Long/*categoryID*/, Set<Long/*Containers.ID*/>> containersByCategoryMap = new HashMap<>();
while (itemsIterator.hasNext()) {
Item item = itemsIterator.next();
Long category = item.getCategory();
Set<Long> containers = item.getContainers();
if (containersByCategoryMap.containsKey(category)) {
Set<Container> containersByCategory = containersByCategoryMap.get(category);
containersByCategory.addAll(containers);
} else {
Set<Container> containersByCategory = new HashSet<>(containers);
containersByCategoryMap.put(category, containersByCategory);
}
}
How can I get the same result using the Stream API?
I tried something like this, but obviously I got duplicate keys exception, since there are multiple items for each category...
containersByCategoryMap = items.stream().collect(Collectors.toMap(item -> item.getCategory(), item -> item.getContainers()));
Eugene :
Since java-9 there is Collectors.flatMapping
:
Map<Long, Set<Long>> map = items.stream()
.collect(Collectors.groupingBy(
Item::getCategory,
Collectors.flatMapping(x -> x.getContainers().stream(), Collectors.toSet())));
Without java-9, you could do:
Map<Long, Set<Long>> result = items.stream()
.flatMap(x -> x.getContainers().stream().map(y -> new SimpleEntry<>(x.getCategory(), y)))
.collect(Collectors.groupingBy(
Entry::getKey,
Collectors.mapping(Entry::getValue, Collectors.toSet())));
You can also do this with Map#merge
:
Map<Long, Set<Long>> map2 = new HashMap<>();
items.forEach(x -> map2.merge(
x.getCategory(),
x.getContainers(),
(left, right) -> {
left.addAll(right);
return left;
}));