当前位置: 首页 > 知识库问答 >
问题:

为什么Java 8“Collector”类是这样设计的?

邢思淼
2023-03-14

我们知道Java 8引入了一个新的流API和Java。util。流动收集器是定义如何聚合/收集数据流的接口。

但是,收集器接口的设计如下:

public interface Collector<T, A, R> {
    Supplier<A> supplier();
    BiConsumer<A, T> accumulator();
    BinaryOperator<A> combiner();
    Function<A, R> finisher();
}

为什么它不是这样设计的?

public interface Collector<T, A, R> {
    A supply();
    void accumulate(A accumulator, T value);
    A combine(A left, A right);
    R finish(A accumulator);
}

后者更容易实现。把它设计成前者的考虑是什么?

共有3个答案

章鸿光
2023-03-14

2相关原因

>

  • 通过组合子的功能组合。(请注意,您仍然可以进行面向对象合成,但请看下面的要点)
  • 当分配目标是功能接口时,可以通过lambda表达式或方法引用在简洁的表达代码中构建业务逻辑。

    功能性成分

    收集器API为通过组合器进行功能组合铺平了道路。i、 e.构建小型/最小的可重用功能,并以有趣的方式将其中一些功能组合成高级特性/功能。

    简洁的表达代码

    下面我们使用函数指针(Employee::getSalary)来填充从Employee对象到int的映射器功能。Summingit填充添加int的逻辑,因此结合在一起,我们在一行声明性代码中写出了工资总和。

    //计算员工工资的总和int总计=employees.stream()收集(Collectors.summingInt(雇佣ee::getSalary)));

  • 蒋寒
    2023-03-14

    组合比继承更受青睐。

    您问题中的第一个模式是模块配置。Collector接口的实现可以为供应商、累加器等提供不同的实现。这意味着可以从现有的供应商、累加器等实现池中组合Collector实现。这也有助于重用,并且两个Collector可能使用相同的Accumulator实现。Stream.collect()使用提供的行为。

    在第二种模式中,收集器实现必须自己实现所有功能。所有类型的变体都需要重写父实现。如果两个采集器在一个步骤(例如,累积)中具有相似的逻辑,则重复使用的范围不多,并且代码重复。

    东深
    2023-03-14

    实际上,它最初的设计与您的提议类似。请参阅project lambda repository中的早期实现(makeResult现在是供应商)。后来更新为当前设计。我认为,这种更新的基本原理是简化收集器组合器。我没有发现关于这个主题的任何具体讨论,但我的猜测得到了一个事实的支持,即映射收集器出现在同一个变更集中。考虑收集器的实现。映射:

    public static <T, U, A, R>
    Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
                               Collector<? super U, A, R> downstream) {
        BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator();
        return new CollectorImpl<>(downstream.supplier(),
                                   (r, t) -> downstreamAccumulator.accept(r, mapper.apply(t)),
                                   downstream.combiner(), downstream.finisher(),
                                   downstream.characteristics());
    }
    

    此实现只需要重新定义累加器函数,保留供应商、合并器和分页器的原样,因此在调用供应商、合并器或分页器时没有额外的间接寻址:只需直接调用原始收集器返回的函数。更重要的是收集,然后:

    public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,
                                                                Function<R,RR> finisher) {
        // ... some characteristics transformations ...
        return new CollectorImpl<>(downstream.supplier(),
                                   downstream.accumulator(),
                                   downstream.combiner(),
                                   downstream.finisher().andThen(finisher),
                                   characteristics);
    }
    

    此处仅更改了分页装订器,但使用了原始供应商、累加器和合路器。由于为每个元素调用了累加器,因此减少间接性可能非常重要。试着用你提出的设计重写映射和收集,然后你就会发现问题所在。新的JDK-9收集器(如过滤和平面映射)也受益于当前的设计。

     类似资料:
    • 我发现Java 8没有与ZonedDateTime等价的功能,而是只与时间(ZonedTime类或类似的东西)一起工作。我知道他们包括OffsetTime类,但它只存储偏移量。 将时区与日期和时间一起存储,而不仅仅是存储偏移量,有助于更轻松地处理夏令时。 我不是要你给我选择,我知道有很多方法;我只是想知道为什么这样的类不包括在内,这是一个设计问题吗?或者他们只是发现它是多余的?

    • 像pdftable和pdfCell,都是操作table的为啥没有单独在pdf packege下面开一个table的包分类呢。 这样大量的类在同一个包下都很难找哇

    • 我正在看宣传单。 在setTimeout中调用

    • 我正在使用一个3d party库,它们返回缺少类型规范的集合(例如,

    • 在添加新的键值对时,我有几个关于重建哈希映射的问题。我将根据这些事实提出问题(它们对于Oracle JVM是正确的,不确定它们对于其他JVM是否正确): 每次当HashMap增长大于阈值(阈值=加载因子*条目数)时,Resize将重建HashMap,使其具有更大的内部表数组。新创建的条目放在哪个存储桶中并不重要,Map仍然会变得更大。即使所有条目都进入一个bucket(即它们的键“返回相同的数字)