Java 8 Stream API:如何将列表转换为Map中有重复键的Map <long,set =“”>?

时间:2018-08-07 09:12:13

标签: java list dictionary java-8 java-stream

我有一个包含以下字段的课程:

public class Item{
   private String name;
   private Long category;
   private Set<Long> containers;
}

我需要做的就是

List<Item> items

变成

Map<Long/*categoryID*/, Set<Long/*Containers*/>>

使用Java 8 Stream API。

现在,我可以使用Itarable和一些if得到相同的结果,就像这样:

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

如何使用Stream API获得相同的结果?

我尝试了类似的方法,但是很明显我得到了重复的键异常,因为每个类别都有多个项目...

containersByCategoryMap = items.stream().collect(Collectors.toMap(item -> item.getCategory(), item -> item.getContainers()));

3 个答案:

答案 0 :(得分:4)

自Java-9起,就有Collectors.flatMapping

Map<Long, Set<Long>> map = items.stream()
            .collect(Collectors.groupingBy(
                    Item::getCategory,
                    Collectors.flatMapping(x -> x.getContainers().stream(), Collectors.toSet())));

没有Java-9,您可以这样做:

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

您也可以使用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;
            }));

答案 1 :(得分:1)

您可以使用带有合并功能的toMap来处理键冲突,

Map<Long, Set<Long>> containersByCategoryMap = items.stream()
    .collect(Collectors.toMap(Item::getCategory,
        Item::getContainers, 
        (s1, s2) -> Stream.concat(s1.stream(), s2.stream()).collect(Collectors.toSet())));

答案 2 :(得分:0)

您可能还对使用Guava及其SetMultimap界面(比Map<?, Set<?>>更易于使用)感兴趣。

如果您希望结果数据结构不可变,则可以使用ImmutableSetMultimap.flatteningToImmutableSetMultimap,如下所示:

SetMultimap<Long, Long> multimap = items.stream()
        .collect(ImmutableSetMultimap.flatteningToImmutableSetMultimap(
                Item::getCategory, item -> item.getContainers().stream()
        ));