我使用groupingBy
、mapping
和reducing
尝试解决以下问题:Elegantly create map with object fields as key/value from object stream in 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,如果有影响的话。
Collectors
中找到flatMapping
(或者应用hobbies.stream()
并与之一起工作)。好知道它即将推出。哦,亲爱的,我把组合器和运算符搞混了。所以,交换参数只是巧合地得到了正确的结果。感谢您的澄清! - Rolandf(previous, next)
,其中previous
将是第一次评估的身份值和后续评估的先前结果。因此,(a,b)->a
将始终以身份值结束,而(a,b)->b
将使用映射器函数创建的新集合。但在并行评估中,两个参数都可以是先前部分评估的结果,由于部分结果可能为空,因此任一参数都可能是身份值,因此使用第二个参数不是可靠的修复方法。 - Holger