如何使用 "same" 收集器使用多个 Stream,而不连接它们?

How to consume more than one Stream with the "same" Collector, without concatenating them?

假设我们有一个 Collector, and we want to feed it the contents of a succession of Streams。

最自然的做法是 concatenating the Streams and feeding the Collector with the concatenation. But this might not be optimal: for example, if each Stream reads from a scarce resource allocated with a try-with-resources,同时拥有所有 Stream 会很昂贵。

此外,有时我们甚至可能无法直接访问 Streams,我们可能只有一个不透明的方法“提供”它作为参数接收的 Collector,并且 returns结果。

在这些情况下,如何从多个来源喂养 Collector

一种解决方案是使用此辅助 duplicate 函数:

static <T,A,R> Collector<T,A,Collector<T,A,R>> duplicate(Collector<T,A,R> collector) {
    final Supplier<A> supplier = collector.supplier();
    final BiConsumer<A, T> accumulator = collector.accumulator();
    final BinaryOperator<A> combiner = collector.combiner();
    final Function<A, R> finisher = collector.finisher();
    final Function<A, Collector<T,A,R>> newFinisher = (finalState) ->
            Collector.of(() -> finalState, accumulator, combiner, finisher);
    return Collector.of(supplier,accumulator,combiner,newFinisher);
}

duplicate 接受一个 Collector 和 returns 一个 Collector 就像第一个一样,除了它不是原始结果类型,而是 returns 另一个 Collector 作为结果,我们稍后可以传递给进一步 Streams:

public static void main( String[] args )
{
    final Collector<Integer, ?, Integer> summy0 =
         Collectors.summingInt(i -> i);

    final Collector<Integer, ?, Integer> summy1 =
         Stream.<Integer>of(1, 2, 3).collect(duplicate(summy0));

    final Collector<Integer, ?, Integer> summy2 =
         Stream.<Integer>of(4, 5, 6).collect(duplicate(summy1));

    System.out.println(Stream.<Integer>of().collect(summy2));
}

而不是 concatenating Streams that have been previously allocated with a try-with-resources, a possible solution is to splice them in a "top-level" Stream using flatMap,然后使用 Collector.

使用生成的 Stream

for safe resource handling with Streams is to use try-with-resources。然而,flatMap 在这方面表现特殊:它本身确保拼接的子 Stream 关闭,both 当它们在主中“耗尽”时Stream,当Stream异常中断时。

在我看来,flatMap javadocs 中的措辞对于异常情况下的清理感觉有点模棱两可:

Each mapped stream is closed after its contents have been placed into this stream.

但是这个实验表明,即使出现异常,sub-Streams 也会关闭:

// This prints "closed!" before the stack trace
Stream.of(1,2,3)
.flatMap((i) ->
     Stream.<String>generate(() -> { throw new RuntimeException(); })
     .limit(2)
     .onClose(() -> System.err.println("closed!"))
).forEach(System.err::println);