代码之家  ›  专栏  ›  技术社区  ›  Blundell

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

  •  11
  • Blundell  · 技术社区  · 6 年前
     Stream<String> a = Stream.of("one", "three", "five");
     Stream<String> b = Stream.of("two", "four", "six");
    

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

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

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

    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
    
    5 回复  |  直到 5 年前
        1
  •  12
  •   Holger    5 年前

    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
  •  2
  •   Eugene    6 年前

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

    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();
    }
    
        3
  •  2
  •   Blundell    6 年前

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

    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);
        }
    
        4
  •  1
  •   Kartik    6 年前

    今年五月 这是一个好答案,因为

    (2) 它不是完全无状态的,因为它使用原子整数。

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

    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 -> 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
    
        5
  •  1
  •   user_3380739    6 年前

    一个解决方案 Iterator

    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);
    

    abacus-util

     AtomicInteger idx = new AtomicInteger();
     StreamEx.merge(a, b, (s1, s2) -> idx.getAndIncrement() % 2 == 0 ? Nth.FIRST : Nth.SECOND).forEach(Fn.println()); 
    
        6
  •  0
  •   ZhekaKozlov    5 年前

    Streams.zip Stream.flatMap :

    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