问题描述
我使用 groupingBy
,映射
和减少
以下问题:。总结的目标是获得一张年龄为关键的地图,以及一个人的爱好为 Set
。
I played around with a solution using groupingBy
, mapping
and reducing
to the following question: Elegantly create map with object fields as key/value from object stream in Java 8. Summarized the goal was to get a map with age as key and the hobbies of a person as a Set
.
我提出的解决方案之一(不好,但不是重点)有一种奇怪的行为。
One of the solutions I came up with (not nice, but that's not the point) had a strange behaviour.
使用以下列表作为输入:
With the following list as input:
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]}
显然不是我所期待的。我更期待这一点:
clearly not what I was expecting. I rather expected this:
map = {23=[a, d, e], 24=[b, c]}
现在如果我只是替换(strings,strings2)的顺序
的二元运算符(减少收集器)到(strings2,字符串)
我得到了预期的结果。那我在这里想念的是什么?
我是否误解了减少
-collector?或者我错过了哪些文档片段显然我的用法没有按预期工作?
Now if I just replace the order of (strings, strings2)
of the binary operator (of the reducing collector) to (strings2, strings)
I get the expected result. So what did I miss here?Did I misinterpret the reducing
-collector? Or which documentation piece did I miss that makes it obvious that my usage was not working as expected?
如果重要的话Java版本是1.8.0_121。
Java version is 1.8.0_121 if that matters.
推荐答案
减少永远不应修改传入的对象。在您的情况下,您正在修改应该是标识值的传入 HashSet
并将其返回,因此所有组都将具有相同的 HashSet
实例作为结果,包含所有值。
Reduction should never modify the incoming objects. In your case, you are modifying the incoming HashSet
that is supposed to be the identity value and return it, so all groups will have the same HashSet
instance as result, containing all values.
您需要的是,可以通过<$ c $实现c> Collector.of(...)就像已经使用预建的收藏家实现的那样 Collectors.toList()
, Collectors.toSet()
等。
What you need is a Mutable Reduction, which can be implemented via Collector.of(…)
like it has been already implemented with the prebuilt collectors Collectors.toList()
, Collectors.toSet()
, etc.
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将引入它。有了它,解决方案将如下所示:
The reason, we need a custom collector at all, is that Java 8 doesn’t have the flatMapping
collector, which Java 9 is going to introduce. With that, the solution will look like:
Map<Integer, Set<String>> map = personList.stream()
.collect(Collectors.groupingBy(o -> o.age,
Collectors.flatMapping(p -> p.hobbies.stream(), Collectors.toSet())));
这篇关于Java 8 stream.collect(... groupingBy(... mapping(... reduction)))减少BinaryOperator的使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!