如何将两个已排序的Observable合并为一个已排序的Observable?

时间:2015-06-03 20:46:14

标签: java system.reactive rx-java

假设:

Integer[] arr1 = {1, 5, 9, 17};
Integer[] arr2 = {1, 2, 3, 6, 7, 12, 15};
Observable<Integer> o1 = Observable.from(arr1);
Observable<Integer> o2 = Observable.from(arr2);

如何获取包含1, 1, 2, 3, 5, 6, 7, 9, 12, 15, 17

的Observable

6 个答案:

答案 0 :(得分:4)

修改:如果您打算使用此功能,请参阅the_joric的评论。有一个边缘情况没有处理,我没有看到一个快速的方法来解决它,所以我没有时间来修复它。

这是C#中的解决方案,因为您拥有system.reactive标记。

static IObservable<int> MergeSorted(IObservable<int> a, IObservable<int> b)
{
    var source = Observable.Merge(
        a.Select(x => Tuple.Create('a', x)),
        b.Select(y => Tuple.Create('b', y)));
    return source.Publish(o =>
    {
        var published_a = o.Where(t => t.Item1 == 'a').Select(t => t.Item2);
        var published_b = o.Where(t => t.Item1 == 'b').Select(t => t.Item2);
        return Observable.Merge(
            published_a.Delay(x => published_b.FirstOrDefaultAsync(y => x <= y)),
            published_b.Delay(y => published_a.FirstOrDefaultAsync(x => y <= x)));
    });
}

这个想法总结如下。

  • a发出值x时,我们会将其延迟,直到b发出值为y的值x <= y

  • b发出值y时,我们会将其延迟,直到a发出值为x的值y <= x

如果您只有热观察,您可以执行以下操作。但如果混合物中存在任何冷可观测量,则以下情况不起作用。我建议总是使用适用于冷热观察的版本。

static IObservable<int> MergeSortedHot(IObservable<int> a, IObservable<int> b)
{
    return Observable.Merge(
        a.Delay(x => b.FirstOrDefaultAsync(y => x <= y)),
        b.Delay(y => a.FirstOrDefaultAsync(x => y <= x)));
}

答案 1 :(得分:4)

您可以合并,排序和展平序列,但这会产生很大的开销:

o1.mergeWith(o2).toSortedList().flatMapIterable(v -> v).subscribe(...)

o1.concatWith(o2).toSortedList().flatMapIterable(v -> v).subscribe(...)

否则,您需要编写一个相当复杂的运算符。

编辑04/06/2015:

Here is运算符可以更有效地执行此排序合并。

答案 2 :(得分:3)

我还在寻找一种支持背压但无法找到它的合并排序解决方案。因此决定基于现有的zip运算符自行实现它。

与zip类似,已排序的合并运算符首先从每个源可观察数据中收集一个项目,然后将它们放入优先级队列中,根据它们的自然顺序从中逐个发出它们。指定比较器。

您可以从GitHub中将其作为随时可用的库获取,或者只是复制/粘贴代码:

https://github.com/ybayk/rxjava-recipes

参见单元测试的使用情况。

答案 3 :(得分:1)

前一段时间在RxJava mailing list上讨论了这个问题,您可以在该主题中找到一些指向可能解决方案的链接。

答案 4 :(得分:0)

如何合并和排序?

sql.Request

您可以在此处查看更多示例

https://github.com/politrons/reactive

答案 5 :(得分:0)

我使用Kotlin中编写的自定义转换器的解决方案:

变形金刚:

import logging

# Only show warnings
logging.getLogger("urllib3").setLevel(logging.WARNING)

# Disable all child loggers of urllib3, e.g. urllib3.connectionpool
logging.getLogger("urllib3").propagate = False

用法:

fun <T> orderedMerge(f2: Flowable<T>, c: Comparator<T>) = FlowableTransformer<T, T> { f1 ->
    val f1Iterator = f1.blockingIterable(1).iterator()
    val f2Iterator = f2.blockingIterable(1).iterator()
    Flowable.generate(
            Callable { null as T? to null as T? },
            BiFunction { (lastF1: T?, lastF2: T?), emitter: Emitter<T> ->
                when {
                    lastF1 != null && f2Iterator.hasNext() -> {
                        val nextF2 = f2Iterator.next()
                        if (c.compare(lastF1, nextF2) <= 0) {
                            emitter.onNext(lastF1)
                            null to nextF2
                        } else {
                            emitter.onNext(nextF2)
                            lastF1 to null
                        }
                    }
                    lastF1 != null -> {
                        emitter.onNext(lastF1)
                        null to null
                    }
                    lastF2 != null && f1Iterator.hasNext() -> {
                        val nextF1 = f1Iterator.next()
                        if (c.compare(nextF1, lastF2) <= 0) {
                            emitter.onNext(nextF1)
                            null to lastF2
                        } else {
                            emitter.onNext(lastF2)
                            nextF1 to null
                        }
                    }
                    lastF2 != null -> {
                        emitter.onNext(lastF2)
                        null to null
                    }
                    f1Iterator.hasNext() && f2Iterator.hasNext() -> {
                        val nextF1 = f1Iterator.next()
                        val nextF2 = f2Iterator.next()
                        if (c.compare(nextF1, nextF2) <= 0) {
                            emitter.onNext(nextF1)
                            null to nextF2
                        } else {
                            emitter.onNext(nextF2)
                            nextF1 to null
                        }
                    }
                    f1Iterator.hasNext() -> {
                        val nextF1 = f1Iterator.next()
                        emitter.onNext(nextF1)
                        null to null
                    }
                    f2Iterator.hasNext() -> {
                        val nextF2 = f2Iterator.next()
                        emitter.onNext(nextF2)
                        null to null
                    }
                    else -> {
                        emitter.onComplete()
                        null to null
                    }
                }
            })
}