使用收集器执行flatMap操作

时间:2017-06-25 18:46:02

标签: java java-8 java-stream

我想了解使用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类只允许我进行映射操作。我还能尝试做什么?

感谢。

2 个答案:

答案 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()收藏家在多级缩减中使用时非常有用,例如groupingBypartitioningBy的下游。

这意味着您可以尽可能地编写任何可能的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取消装箱Longlong

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))
        )
    ));
}