如何交错(合并)两个 Java 8 流?

 Stream<String> a = Stream.of("one", "three", "five");
 Stream<String> b = Stream.of("two", "four", "six");

我需要做什么才能使输出如下所示?

// one
// two
// three
// four
// five
// six

我研究了一下,但正如javadoc所解释的那样,它只是一个接一个地附加,它不会交错/穿插。concat

Stream<String> out = Stream.concat(a, b);
out.forEach(System.out::println);

创建一个懒惰串联的流,其元素是第一个流的所有元素,后跟第二个流的所有元素。

错误地给予

 // one
 // three
 // five
 // two
 // four
 // six

如果我收集它们并迭代,可以做到这一点,但希望有更多的Java8-y,Streamy:-)

注意

我不想压缩流

“zip”操作将从每个集合中获取一个元素并将它们组合在一起。

zip操作的结果将如下所示:(不需要的)

 // onetwo
 // threefour
 // fivesix

答案 1

我会使用这样的东西:

public static <T> Stream<T> interleave(Stream<? extends T> a, Stream<? extends T> b) {
    Spliterator<? extends T> spA = a.spliterator(), spB = b.spliterator();
    long s = spA.estimateSize() + spB.estimateSize();
    if(s < 0) s = Long.MAX_VALUE;
    int ch = spA.characteristics() & spB.characteristics()
           & (Spliterator.NONNULL|Spliterator.SIZED);
    ch |= Spliterator.ORDERED;

    return StreamSupport.stream(new Spliterators.AbstractSpliterator<T>(s, ch) {
        Spliterator<? extends T> sp1 = spA, sp2 = spB;

        @Override
        public boolean tryAdvance(Consumer<? super T> action) {
            Spliterator<? extends T> sp = sp1;
            if(sp.tryAdvance(action)) {
                sp1 = sp2;
                sp2 = sp;
                return true;
            }
            return sp2.tryAdvance(action);
        }
    }, false);
}

它尽可能保留输入流的特征,从而允许某些优化(例如,对于 和 )。此外,它添加了即使输入流可能是无序的,以反映交错。count()toArray()ORDERED

当一个流的元素数多于另一个流时,其余元素将显示在末尾。


答案 2

一个比Holger愚蠢的解决方案,但可能符合您的要求:

private static <T> Stream<T> interleave(Stream<T> left, Stream<T> right) {
    Spliterator<T> splLeft = left.spliterator();
    Spliterator<T> splRight = right.spliterator();

    T[] single = (T[]) new Object[1];

    Stream.Builder<T> builder = Stream.builder();

    while (splRight.tryAdvance(x -> single[0] = x) && splLeft.tryAdvance(builder)) {
        builder.add(single[0]);
    }

    return builder.build();
}