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

如何交错(合并)两个Java8流?,java,java-8,functional-programming,java-stream,Java,Java 8,Functional Programming,Java Stream,我查看了concat,但正如javadoc所解释的,它只是一个接一个地追加,而不是交错/穿插 // one // two // three // four // five // six 如果我收集它们并进行迭代,就可以做到这一点,但我希望得到更多的Java8-y,Streamy:-) 注意 我不想让溪流断流 “zip”操作将从每个集合中获取一个元素并将它们组合起来 zip操作的结果如下:(不需要) 我会用这样的方法: // onetwo // threefour // fivesix

我查看了
concat
,但正如javadoc所解释的,它只是一个接一个地追加,而不是交错/穿插

// one
// two
// three
// four
// five
// six
如果我收集它们并进行迭代,就可以做到这一点,但我希望得到更多的Java8-y,Streamy:-)

注意

我不想让溪流断流

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

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


我会用这样的方法:

 // onetwo
 // threefour
 // fivesix

publicstaticstreaminterleave(Stream这可能不是一个好答案,因为
(1) 它收集到地图,我猜这是你不想做的,
(2) 它不是完全无状态的,因为它使用原子整数

仍在添加它,因为
(1) 它可读且
(2) 社区可以从中获得想法,并尝试改进它

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);
}
@Holger的编辑

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

AtomicInteger i = new AtomicInteger(0);
AtomicInteger j = new AtomicInteger(1);

Stream.of(a.collect(Collectors.toMap(o -> i.addAndGet(2), Function.identity())),
        b.collect(Collectors.toMap(o -> j.addAndGet(2), Function.identity())))
        .flatMap(m -> m.entrySet().stream())
        .sorted(Comparator.comparing(Map.Entry::getKey))
        .forEach(e -> System.out.println(e.getValue())); // or collect
one
two
three
four
five
six
Stream.concat(a.map(o->newAbstractMap.SimpleEntry(i.addAndGet(2),o)),
b、 map(o->newAbstractMap.SimpleEntry(j.addAndGet(2),o)))
.sorted(Map.Entry.comparingByKey())
.forEach(e->System.out.println(e.getValue());//或collect

这是一个比霍尔格更愚蠢的解决方案,但可能符合您的要求:

Stream.concat(a.map(o -> new AbstractMap.SimpleEntry<>(i.addAndGet(2), o)),
        b.map(o -> new AbstractMap.SimpleEntry<>(j.addAndGet(2), o)))
        .sorted(Map.Entry.comparingByKey())
        .forEach(e -> System.out.println(e.getValue())); // or collect
私有静态流交织(流左、流右){
Spliterator spleft=left.Spliterator();
Spliterator splRight=right.Spliterator();
T[]single=(T[])新对象[1];
Stream.Builder=Stream.Builder();
while(splRight.tryAdvance(x->single[0]=x)和&splLeft.tryAdvance(builder)){
builder.add(单个[0]);
}
返回builder.build();
}

正如您从问题评论中所看到的,我尝试使用zip:

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();
}
streama=一、三、五流;
b流=第二流、第四流、第六流;
流出=交织(a,b);
公共静态流交织(流streamA、流streamB){
返回zip(streamA,streamB,(o1,o2)->Stream.of(o1,o2)).flatMap(s->s);
}
/**
* https://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip
**/
专用静态流拉链(流streamA、流streamB、双功能拉链){
final Iterator Iterator=streamA.Iterator();
final Iterator iteratorB=streamB.Iterator();
final Iterator Iterator=new Iterator(){
@凌驾
公共布尔hasNext(){
返回iteratorA.hasNext()&&iteratorB.hasNext();
}
@凌驾
公共C next(){
返回zippers.apply(iteratorA.next(),iteratorB.next());
}
};
最终布尔并行=streamA.isParallel()| | streamB.isParallel();
返回iteratorofinitestream(iteratorC,parallel);
}
私有静态流迭代器of initestream(迭代器迭代器,布尔并行){
最终Iterable Iterable=()->迭代器;
返回StreamSupport.stream(iterable.spliterator(),parallel);
}

一个带有迭代器的解决方案

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

Stream<String> out = interleave(a, b);


    public static <T> Stream<T> interleave(Stream<T> streamA, Stream<T> streamB) {
        return zip(streamA, streamB, (o1, o2) -> Stream.of(o1, o2)).flatMap(s -> s);
    }

    /**
    * https://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip
    **/
    private static <A, B, C> Stream<C> zip(Stream<A> streamA, Stream<B> streamB, BiFunction<A, B, C> zipper) {
        final Iterator<A> iteratorA = streamA.iterator();
        final Iterator<B> iteratorB = streamB.iterator();
        final Iterator<C> iteratorC = new Iterator<C>() {
            @Override
            public boolean hasNext() {
                return iteratorA.hasNext() && iteratorB.hasNext();
            }

            @Override
            public C next() {
                return zipper.apply(iteratorA.next(), iteratorB.next());
            }
        };
        final boolean parallel = streamA.isParallel() || streamB.isParallel();
        return iteratorToFiniteStream(iteratorC, parallel);
    }

    private static <T> Stream<T> iteratorToFiniteStream(Iterator<T> iterator, boolean parallel) {
        final Iterable<T> iterable = () -> iterator;
        return StreamSupport.stream(iterable.spliterator(), parallel);
    }
使用番石榴和
Stream.flatMap

 AtomicInteger idx = new AtomicInteger();
 StreamEx.merge(a, b, (s1, s2) -> idx.getAndIncrement() % 2 == 0 ? Nth.FIRST : Nth.SECOND).forEach(Fn.println()); 
印刷品:

interleaved.forEach(System.out::println);

zip用于组合元素,我不想组合元素,我想保持相同的元素总数为什么zip不保持相同的元素总数?阅读另一条线索,zip总是使用zip函数来组合每个流中的一个元素以生成一个新元素。我只想交错而不是Zipf,以防任何人来在未来,这里是评论+重定向的答案:我喜欢创建
交错
方法,它本质上包装了
zip
方法以提高可读性等。我已经投票重新打开,所以你可以在这里发布,而不是在外部发布……你不需要收集到地图中,因为你只对获取感兴趣调整条目流,这样您就可以简单地使用
stream.concat(a.map(o->new AbstractMap.SimpleEntry(i.addAndGet(2),o))、b.map(o->new AbstractMap.SimpleEntry(j.addAndGet(2,o))
来获取它。然后,您可以链接
.sorted(map.Entry.comparingByKey())
。但是你是对的,这种可变状态是不被鼓励的。最值得注意的是,它在并行执行方面会有问题。@Holger谢谢你,在答案中添加了这个。我之前考虑过,但找不到
入口集
构造函数,懒得用谷歌搜索如何创建入口集:(它不是创建一个
EntrySet
,而是一个
Entry
实例流。现成的实现确实不容易找到(在
AbstractMap
中还有一个
SimpleImmutableEntry
)。从Java 9开始,您可以简单地使用
Map.Entry(key,value)
获取一个不可变的
条目
实例,但您必须知道它不支持
null
键或值,因此您只能在可以排除
null
时使用它。当
的元素多于
时,这不一致地包括
的所有元素,但会删除
的元素e> 右
当它有多于左
时。您应该决定。若要包含所有公共元素,请使用
do{}while(spleft.tryAdvance(builder)和&spRight.tryAdvance(builder));
,然后决定。如果要在流大小不同时包含所有元素,请执行
(spleft.tryAdvance(builder)?spleft:spRight).forEachRemaining(builder);
在循环之后。而且,
Stream.builder
已经方便地实现了
Consumer
。@Holger确实我只想要普通的,但现在问题更严重了,因为
do{}while(splLeft.tryAdvance(builder)&spRight.tryAdvance(builder));
仍将从
左侧
中提取一个元素,因此它仍然不正确:(.我现在看到的更大的问题是
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");

Stream<String> out = interleave(a, b);


    public static <T> Stream<T> interleave(Stream<T> streamA, Stream<T> streamB) {
        return zip(streamA, streamB, (o1, o2) -> Stream.of(o1, o2)).flatMap(s -> s);
    }

    /**
    * https://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip
    **/
    private static <A, B, C> Stream<C> zip(Stream<A> streamA, Stream<B> streamB, BiFunction<A, B, C> zipper) {
        final Iterator<A> iteratorA = streamA.iterator();
        final Iterator<B> iteratorB = streamB.iterator();
        final Iterator<C> iteratorC = new Iterator<C>() {
            @Override
            public boolean hasNext() {
                return iteratorA.hasNext() && iteratorB.hasNext();
            }

            @Override
            public C next() {
                return zipper.apply(iteratorA.next(), iteratorB.next());
            }
        };
        final boolean parallel = streamA.isParallel() || streamB.isParallel();
        return iteratorToFiniteStream(iteratorC, parallel);
    }

    private static <T> Stream<T> iteratorToFiniteStream(Iterator<T> iterator, boolean parallel) {
        final Iterable<T> iterable = () -> iterator;
        return StreamSupport.stream(iterable.spliterator(), parallel);
    }
final Iterator<String> iterA = a.iterator();
final Iterator<String> iterB = b.iterator();

final Iterator<String> iter = new Iterator<String>() {
  private final AtomicInteger idx = new AtomicInteger();
  @Override
  public boolean hasNext() { 
    return iterA.hasNext() || iterB.hasNext();
  }
  @Override
  public String next() {
    return idx.getAndIncrement() % 2 == 0 && iterA.hasNext() ? iterA.next() : iterB.next();
  }
};

 // Create target Stream with StreamEx from: https://github.com/amaembo/streamex    
 StreamEx.of(iter).forEach(System.out::println);

 // Or Streams from Google Guava
 Streams.stream(iter).forEach(System.out::println);
 AtomicInteger idx = new AtomicInteger();
 StreamEx.merge(a, b, (s1, s2) -> idx.getAndIncrement() % 2 == 0 ? Nth.FIRST : Nth.SECOND).forEach(Fn.println()); 
Stream<String> interleaved = Streams
        .zip(a, b, (x, y) -> Stream.of(x, y))
        .flatMap(Function.identity());
interleaved.forEach(System.out::println);
one
two
three
four
five
six