Java 8 个流将 List 项缩减并合并到 Map

Java 8 streams reduce and combine List items to Map

我需要使用 name1/2 的键和值 value1/2 的总和创建映射。

使用 java 8 个流重写此代码的最简洁方法是什么?

class Item {

    private String name1;
    private Integer value1;
    private String name2;
    private Integer value2;

    public Item(final String name1, final Integer value1, final String name2, final Integer value2) {
        this.name1 = name1;
        this.value1 = value1;
        this.name2 = name2;
        this.value2 = value2;
    }
    //getters and setters
}
List<Item> list = Lists.newArrayList(
                new Item("aaa", 1, "bbb", 2),
                new Item("bbb", 5, "ccc", 3),
                new Item("aaa", 8, "bbb", 7),
                new Item("bbb", 2, "aaa", 5));

Map<String, Integer> map = Maps.newHashMap();

for (Item item : list) {
    map.merge(item.name1, item.value1, Integer::sum);
    map.merge(item.name2, item.value2, Integer::sum);
}

System.out.println(map);//{aaa=14, ccc=3, bbb=16}

一个可能的解决方案是将每个项目平面映射到一个由两个条目组成的流中:每个条目将由名称和相应的值组成。然后,通过将具有相同键的值的值相加,将其收集到映射中。由于没有内置的对来保存这两个值,我们可以使用 AbstractMap.SimpleEntry.

Map<String, Integer> map =
    list.stream()
        .flatMap(i -> Stream.of(new AbstractMap.SimpleEntry<>(i.name1, i.value1), new AbstractMap.SimpleEntry<>(i.name2, i.value2)))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, Integer::sum));

或者,使用自定义 collect 调用可能更简单。累加器合并每个键,就像 for 循环体中的一样。棘手的部分是组合器,在这种情况下,它通过迭代第二个映射的条目并将它们合并到第一个映射中来将两个映射合并在一起。

Map<String, Integer> map =
        list.stream()
            .collect(
                HashMap::new,
                (m, i) -> {
                    m.merge(i.name1, i.value1, Integer::sum);
                    m.merge(i.name2, i.value2, Integer::sum);
                },
                (m1, m2) -> m2.forEach((k, v) -> m1.merge(k, v, Integer::sum))
            );