[英]Multi-level map using stream() collect() groupingBy() and reducing()
[英]Java 8 stream.collect( … groupingBy ( … mapping( … reducing ))) reducing BinaryOperator-usage
我使用groupingBy
, mapping
和reducing
解决了以下问题的解决方案: 在Java 8中优雅地创建带有对象字段的映射作为对象流的键/值 。 总结的目的是为了获得与年龄键和一个人作为的爱好地图Set
。
我提出的解决方案之一(不好,但这不是重点)有一个奇怪的行为。
使用以下列表作为输入:
List<Person> personList = Arrays.asList(
new Person(/* name */ "A", /* age */ 23, /* hobbies */ asList("a")),
new Person("BC", 24, asList("b", "c")),
new Person("D", 23, asList("d")),
new Person("E", 23, asList("e"))
);
以及以下解决方案:
Collector<List<String>, ?, Set<String>> listToSetReducer = Collectors.reducing(new HashSet<>(), HashSet::new, (strings, strings2) -> {
strings.addAll(strings2);
return strings;
});
Map<Integer, Set<String>> map = personList.stream()
.collect(Collectors.groupingBy(o -> o.age,
Collectors.mapping(o -> o.hobbies, listToSetReducer)));
System.out.println("map = " + map);
我有:
map = {23=[a, b, c, d, e], 24=[a, b, c, d, e]}
显然不是我所期待的。 我更期待这个:
map = {23=[a, d, e], 24=[b, c]}
现在,如果我只是将二进制运算符(减少收集器的) (strings, strings2)
的顺序替换为(strings2, strings)
我得到预期的结果。 那我在这里想念的是什么? 我是否误解了reducing
收集器? 或者我错过了哪些文档片段,很明显我的用法没有按预期工作?
如果重要的话,Java版本是1.8.0_121。
减少不应该修改传入的对象。 在您的情况下,您正在修改应该是标识值的传入HashSet
并将其返回,因此所有组将具有相同的HashSet
实例,其中包含所有值。
你需要的是一个可变减少 ,它可以通过Collector.of(…)
实现,就像它已经使用预构建的收集器Collectors.toList()
, Collectors.toSet()
等实现的一样。
Map<Integer, Set<String>> map = personList.stream()
.collect(Collectors.groupingBy(o -> o.age,
Collector.of(HashSet::new, (s,p) -> s.addAll(p.hobbies), (s1,s2) -> {
s1.addAll(s2);
return s1;
})));
我们需要一个自定义收集器的原因是Java 8没有flatMapping
收集器,Java 9将引入它。 有了它,解决方案将如下所示:
Map<Integer, Set<String>> map = personList.stream()
.collect(Collectors.groupingBy(o -> o.age,
Collectors.flatMapping(p -> p.hobbies.stream(), Collectors.toSet())));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.