【问题标题】:Merge Two Streams合并两个流
【发布时间】:2014-05-23 08:36:35
【问题描述】:

我正在尝试实现一种方法,以基于 Comparator 的值合并两个 Streams 中的值。

我有办法做到这一点,我遍历流并将值插入Stream.Builder,但我无法弄清楚如何制作延迟评估版本(许多流操作的方式are),因此它也可以处理无限流。

我想要它做的只是对输入数据执行 single 合并传递, 对流进行排序(实际上,流很可能是无序;这种无序需要保留)。

static Stream<E> merge(Stream<E> first, Stream<E> second, Comparator<E> c)

我怎样才能像这样懒惰地合并两个流?

如果我用两个Queues 作为输入和一些Consumer 作为输出来做这件事,那将相当简单:

void merge(Queue<E> first, Queue<E> second, Consumer<E> out, Comparator<E> c){
    while(!first.isEmpty() && !second.isEmpty()
        if(c.compare(first.peek(), second.peek()) <= 0)
            out.accept(first.remove());
        else
            out.accept(second.remove());
    for(E e:first)
        out.accept(e);
    for(E e:second)
        out.accept(e);
}

但我需要通过惰性评估和流来做到这一点。

为了解决 cmets,以下是一些示例输入和结果:

示例 1:

merge(
    Stream.of(1, 2, 3, 1, 2, 3),
    Stream.of(2, 2, 3, 2, 2, 2),
    Comparator.naturalOrder()
);

会返回一个会产生这个序列的流:

1, 2, 2, 2, 3, 3, 1, 2, 2, 2, 2, 3

示例 2:

merge(
    Stream.iterate(5, i->i-1),
    Stream.iterate(1, i->i+1),
    Comparator.naturalOrder()
);

将返回一个无限的(嗯,一个INT_MAX + 5 项目)流,它会产生序列:

1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 0, -1 ...

如您所见,这不仅仅是concat(first,second).sort(),因为 (a) 您无法对无限流进行排序,并且 (b) 即使您可以对流进行排序,它也不会给出所需的结果。

【问题讨论】:

  • 你不能真正合并它们,因为除非你的问题没有说明一切,否则原始流都没有被排序;这意味着您无法提前知道要从流 1 读取的元素是否应该在来自同一流的另一个元素之前有效地被馈送,并且您对流 2 也有同样的问题。除了吞下它们之外,您是否真的期望存在解决方案和排序?
  • 除了Stream.concat(first, second).sorted(c);我不确定你能做多少......
  • 我真的不明白这应该做什么。 @AJMansfield,您能否给出您期望的输入和输出示例?根据你的意思,这可能不是那么绝望,但我不能说。 “合并”流是什么意思?如果它是,例如,从排序导入合并排序,它是可行的。
  • @LouisWasserman 在我看来他是在成对比较元素,即结果的前两个元素是每个流的第一个元素(以一个或另一个顺序),接下来的两个元素是每个流的第二个元素(一种或另一种)等......好吧,第一个示例遵循该模式。第二个破坏了我的猜想。没关系。

标签: java merge java-8 java-stream


【解决方案1】:

您需要实现Spliterator,而不是通过Stream.Builder。为此,您甚至可以通过Iterator,因为它是一个相当顺序的操作。轻用番石榴,

return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
    Iterators.mergeSorted(
      Arrays.asList(stream1.iterator(), stream2.iterator()),
      comparator),
    Spliterator.ORDERED),
  false /* not parallel */ );

【讨论】:

  • 我一定遗漏了一些东西,但我不明白这如何解决 OP 的问题?
  • 鉴于OP的更新解释,这实际上是一个正确的答案。
  • 不应该是Spliterator.ORDERED吗?
  • 解决方案看起来不错,但问题很奇怪。您也可以取而代之的是源流拆分器,如果它们都知道大小,您可以对其求和并通过Spliterators.spliterator 创建。这样,生成的拆分器会更好地拆分,并且像 toArray() 这样的操作会更有效地工作。
【解决方案2】:

来自 Guava 的 Iterables.mergeSorted()

public static <T> Iterable<T> mergeSorted(Iterable<? extends Iterable<? extends T>> iterables,
                Comparator<? super T> comparator)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-16
    • 2020-03-20
    • 2023-03-26
    • 2018-05-02
    • 2013-03-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多