【问题标题】:Why is the combiner of the Collector interface not consistent with the overloaded collect method?为什么 Collector 接口的 combiner 与重载的 collect 方法不一致?
【发布时间】:2015-07-30 07:22:36
【问题描述】:

接口Stream<T>中有一个重载方法collect(),签名如下:

<R> R collect(Supplier<R> supplier,
          BiConsumer<R,? super T> accumulator,
          BiConsumer<R,R> combiner)

还有另一个版本的collect(Collector&lt;? super T,A,R&gt; collector),它接收具有前面三个函数的对象。 combiner对应的接口Collector的属性签名为BinaryOperator&lt;A&gt; combiner()

在后一种情况下,Java API 8 声明:

组合器函数可以将状态从一个参数折叠到另一个参数并返回,或者可以返回一个新的结果容器。

为什么以前的collect 方法也没有收到BinaryOperator&lt;R&gt;

【问题讨论】:

    标签: java java-8 java-stream


    【解决方案1】:

    collect 的“内联”(3-arg) 版本是专为您已经拥有这些功能而设计的。例如:

    ArrayList<Foo> list = stream.collect(ArrayList::new, 
                                         ArrayList::add,
                                         ArrayList::addAll);
    

    或者

    BitSet bitset = stream.collect(BitSet::new, 
                                   BitSet::set,
                                   BitSet::or);
    

    虽然这些只是激励性示例,但我们对现有类似构建器类的探索是,现有组合器候选者的签名更适合转换为 BiConsumer,而不是 BinaryOperator。提供您所要求的“灵活性”将使这种重载在它旨在支持的情况下变得不那么有用 - 这是当您已经拥有这些功能并且您不想让 (或学习制作)收集器只是为了收集它们。

    另一方面,收集器具有更广泛的用途,因此具有额外的灵活性。

    【讨论】:

    • 关于收集的另一个问题 - stackoverflow.com/questions/30310749/…
    • StringBuilder::append 是一个不好的例子,因为它确实返回了容器。但BitSet 将是一个不错的候选人。
    • 感谢@Holger,已修复。
    • 看起来combiner 的内联版本的文档缺少一个关键细节:哪一方应该收到合并的结果? BinaryOperator 版本可以将其留给实现者,但 BiConsumer 需要指定哪个是主要的。
    【解决方案2】:

    请记住,Stream.collect() 的主要目的是支持Mutable Reduction。对于这个操作,accumulatorcombiner 这两个函数都是用来操作可变容器的,不需要返回值。

    因此,不坚持返回值要方便得多。正如Brian Goetz has pointed out,这个决定允许重用许多现有的容器类型及其方法。如果不能直接使用这些类型,整个三参数collect 方法将毫无意义。

    相比之下,Collector 接口是此操作的抽象,支持更多用例。最值得注意的是,您甚至可以使用值类型(或具有值类型语义的类型)via a Collector 对普通的(即不可变的)Reduction 操作进行建模。在这种情况下,必须有一个返回值,因为值对象本身不能被修改。

    当然,它并不是要用作stream.collect(Collectors.reducing(…)) 而不是stream.reduce(…)。相反,当组合收集器时,这种抽象会派上用场,例如喜欢groupingBy(…,reducing(…))

    【讨论】:

      【解决方案3】:

      如果之前的collect 方法接收到BinaryOperator&lt;R&gt;,那么下面的示例将无法编译:

      ArrayList<Foo> list = stream.collect(ArrayList::new, 
                                           ArrayList::add,
                                           ArrayList::addAll);
      

      在这种情况下,编译器无法推断 combiner 的返回类型,并会给出编译错误。

      因此,如果此版本的 collect 方法与 Collector 接口一致,那么它将促进此版本的 collect 方法的更复杂的使用,这不是有意的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-21
        • 2012-11-23
        • 1970-01-01
        • 2021-03-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多