为什么原始流没有 collect(Collector)?

2022-09-01 09:54:55

我正在为新手程序员编写一个库,所以我试图保持API尽可能干净。

我的库需要做的一件事是对大量整数或多头集合执行一些复杂的计算。我的用户需要从中计算这些值,这些方案和业务对象很多,所以我认为最好的方法是使用流来允许用户将业务对象映射到收集器,然后计算收集器内部的计算。IntStreamLongStream

但是,IntStream 和 LongStream 只有 3 个参数收集方法:

collect(Supplier<R> supplier, ObjIntConsumer<R> accumulator, BiConsumer<R,R> combiner)

并且没有更简单的方法。collect(Collector)Stream<T>

所以不是能够做到

Collection<T> businessObjs = ...
MyResult result = businessObjs.stream()
                              .mapToInt( ... )
                              .collect( new MyComplexComputation(...));

我必须提供这样的供应商,累加器和组合器:

MyResult result = businessObjs.stream()
                              .mapToInt( ... )
                              .collect( 
                                  ()-> new MyComplexComputationBuilder(...),
                                  (builder, v)-> builder.add(v),
                                  (a,b)-> a.merge(b))
                              .build(); //prev collect returns Builder object

对于我的新手用户来说,这太复杂了,而且非常容易出错。

我的解决方法是创建静态方法,这些方法将 or 作为输入,并为您隐藏收集器的创建和执行IntStreamLongStream

public static MyResult compute(IntStream stream, ...){
       return .collect( 
                        ()-> new MyComplexComputationBuilder(...),
                        (builder, v)-> builder.add(v),
                        (a,b)-> a.merge(b))
               .build();
}

但这并没有遵循使用Streams的正常惯例:

IntStream tmpStream = businessObjs.stream()
                              .mapToInt( ... );

 MyResult result = MyUtil.compute(tmpStream, ...);

因为您必须保存一个临时变量并将其传递给静态方法,或者在静态调用中创建 Stream,当它与其他参数混合到我的计算中时,这可能会令人困惑。

有没有一种更清洁的方法来做到这一点,同时仍然使用 或 ?IntStreamLongStream


答案 1

事实上,我们确实制作了一些专业化的原型。我们发现 - 除了更专业类型的明显烦恼之外 - 如果没有完整的原始专业集合(如Trove或GS-Collections,但JDK没有),这并不是很有用。例如,如果没有IntArrayList,Collector.OfInt只是将拳击推向其他地方 - 从收集器到容器 - 这并没有太大的胜利,而且有更多的API表面。Collector.OfXxx


答案 2

也许如果使用方法引用而不是 lambdas,则原始流收集所需的代码看起来不会那么复杂。

MyResult result = businessObjs.stream()
                              .mapToInt( ... )
                              .collect( 
                                  MyComplexComputationBuilder::new,
                                  MyComplexComputationBuilder::add,
                                  MyComplexComputationBuilder::merge)
                              .build(); //prev collect returns Builder object

在 Brian 对这个问题的最终回答中,他提到了另外两个 Java 集合框架,它们确实具有原始集合,实际上可以与原始流上的 collect 方法一起使用。我认为说明一些示例来说明如何在这些框架中使用原始容器和原始流可能会有所帮助。下面的代码也适用于并行流。

// Eclipse Collections
List<Integer> integers = Interval.oneTo(5).toList();

Assert.assertEquals(
        IntInterval.oneTo(5),
        integers.stream()
                .mapToInt(Integer::intValue)
                .collect(IntArrayList::new, IntArrayList::add, IntArrayList::addAll));

// Trove Collections

Assert.assertEquals(
        new TIntArrayList(IntStream.range(1, 6).toArray()),
        integers.stream()
                .mapToInt(Integer::intValue)
                .collect(TIntArrayList::new, TIntArrayList::add, TIntArrayList::addAll));

注意:我是 Eclipse Collections 的提交者。


推荐