Java 8 Stream API: how to convert a List to a Map<Long, Set> having repeated keys in the List?

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;
            }));

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=96306&siteId=1