我想了解使用flatMap
时执行Collector
的方法。这是一个例子。
情境:
我有以下界面:
interface Ic {
//empty
}
interface Ib {
Stream<Ic> getCs();
}
interface Ia {
String getName();
Stream<Ib> getBs();
}
我正在尝试实施以下方法:
Map<String, Long> total_of_C_per_A (Stream<Ia> streamOfA) {
return streamOfA.collect(groupBy(Ia::getName, ???));
}
分类函数非常简单,我的问题在于下游收集器。我需要计算与“A”相关的“C”数。
我尝试过:
如果我想简单地返回计数而不创建地图,我会这样做:
streamOfA
.flatMap(Ia::getBs)
.flatMap(Ib::getCs)
.count();
但是Collectors
类只允许我进行映射操作。我还能尝试做什么?
感谢。
答案 0 :(得分:6)
This answer指出你已经朝着正确的方向发展,但是没有必要嵌套多个mapping
收集器,因为你可以将这些函数写入一个lambda表达式。考虑到summingLong
收集器需要一个计算结果为long
的函数,您可以简单地将该函数传递给收集器,而不需要任何mapping
收集器:
Map<String, Long> total_of_C_per_A (Stream<Ia> streamOfA) {
return streamOfA.collect(groupingBy(
Ia::getName,
summingLong(ia -> ia.getBs().flatMap(Ib::getCs).count())));
}
这样做的另一个好处是long
值不会加到Long
个实例。
还有flatMap
的替代方案:
Map<String, Long> total_of_C_per_A (Stream<Ia> streamOfA) {
return streamOfA.collect(groupingBy(
Ia::getName,
summingLong(ia -> ia.getBs().mapToLong(ib -> ib.getCs().count()).sum())));
}
答案 1 :(得分:5)
文档将Collectors#mapping描述为:
通过在累积之前将映射函数应用于每个输入元素,将收集器接受类型
U
的元素接受到一个接受T
类型的元素。
mapping()
收藏家在多级缩减中使用时非常有用,例如groupingBy
或partitioningBy
的下游。
这意味着您可以尽可能地编写任何可能的Collector
。
import static java.util.stream.Collectors.*;
Map<String, Long> total_of_C_per_A(Stream<Ia> streamOfA) {
return streamOfA.collect(groupingBy(
Ia::getName,
mapping(
Ia::getBs,
mapping(
it -> it.flatMap(Ib::getCs),
// reduce() does boxing & unboxing ---v
mapping(Stream::count, reducing(0L,Long::sum))
)
)
));
}
或改为使用Collectors#summingLong。
Map<String, Long> total_of_C_per_A(Stream<Ia> streamOfA) {
return streamOfA.collect(groupingBy(
Ia::getName,
mapping(
Ia::getBs,
mapping(
it -> it.flatMap(Ib::getCs),
// summingLong() does boxing ---v
mapping(Stream::count, summingLong(Long::longValue))
// Long::longValue does unboxing operation ---^
)
)
));
}
感谢@Holger指出上述代码的潜在问题,您只需使用summingLong(Stream::count)
即可。在这种方法中,不需要将Stream#count
归档到long
Long
。并Long::longValue
取消装箱Long
至long
。
Map<String, Long> total_of_C_per_A(Stream<Ia> streamOfA) {
return streamOfA.collect(groupingBy(
Ia::getName,
mapping(
Ia::getBs,
// summingLong() doesn't any boxing ---v
mapping(it -> it.flatMap(Ib::getCs), summingLong(Stream::count))
)
));
}