为什么原始Stream没有收集(收藏家)?

dka*_*zel 30 java api-design java-8 java-stream

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

我的库需要做的一件事就是对大量的int或long进行一些复杂的计算.我的用户需要从中计算这些值所需的大量场景和业务对象,因此我认为最好的方法是使用流来允许用户将业务对象映射到IntStream或者LongStream然后计算收集器内的计算.

但是IntStream和LongStream只有3参数collect方法:

collect(Supplier<R> supplier, ObjIntConsumer<R> accumulator, BiConsumer<R,R> combiner)
Run Code Online (Sandbox Code Playgroud)

而且没有简单的collect(Collector)方法Stream<T>.

所以不是能够做到的

Collection<T> businessObjs = ...
MyResult result = businessObjs.stream()
                              .mapToInt( ... )
                              .collect( new MyComplexComputation(...));
Run Code Online (Sandbox Code Playgroud)

我必须提供像这样的供应商,蓄电池和组合器:

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

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

我的工作是创建一个静态方法,它接受IntStreamLongStream作为输入,并为您隐藏收集器的创建和执行

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

但这并不符合使用Streams的常规惯例:

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

 MyResult result = MyUtil.compute(tmpStream, ...);
Run Code Online (Sandbox Code Playgroud)

因为您必须保存临时变量并将其传递给静态方法,或者在静态调用中创建Stream,这可能会在将其与我的计算中的其他参数混合时产生混淆.

有没有更清洁的方法来做到这一点,但仍在使用IntStreamLongStream

Bri*_*etz 25

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

  • 你正在看流的错误结束.操作int时,IntStream不会装箱.但是,如果没有装箱,你可以将容器放入什么样的结果?不是ArrayList <Integer>,或者是HashSet <Integer,Integer>,或者......如果没有一组丰富的int友好内容,Collector.OfInt就无用了. (9认同)
  • @ bayou.io你不必被说服.问题是"他们为什么不这样做",答案是"我们对其进行了原型设计,并得出结论认为,"努力,成本,复杂性"的回归并不存在." (5认同)
  • @dkatzel对,IntStream和朋友们的全部观点是不能对每个操作进行包装,你确实得到了.如果我们有原始友好的集合,那么Collector.OfXxx会更有意义,但我们没有那些,所以我们停在专业化产生实用价值的地方. (4认同)
  • @davidalayachew 是的,但这需要几个步骤。专用仿制药将在稍后的步骤中出现,因此您的答案最终将是正确的,但不会立即正确。(一旦我们有了专门的仿制药,我们就会付出额外的努力来推动像“IntStream”这样的手工专业化。)这一切都需要一段时间才能完全实现。 (2认同)

Don*_*aab 6

也许如果使用方法引用而不是lambdas,原始流收集所需的代码似乎并不复杂.

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

在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));
Run Code Online (Sandbox Code Playgroud)

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