RxJava滑动窗口

时间:2017-08-21 08:56:07

标签: java kotlin rx-java observable reactivex

我有一个可观察的数据发出数据,我想最初缓冲它三秒钟,然后在初始缓冲区之后必须有一秒的滑动。这更像是buffer(timespan,unit,skip),其中跳过时间跨度。

样品:

ObservableData,TimeStamp : (5,1),(10,1.5),(30,2.8),(40,3.2),(60,3.8),(90,4.2)

ExpectedList : {5,10,30},{10,30,40,60},{30,40,60,90}

我可以通过创建自定义运算符来实现此目的。我只是想知道有没有办法在不依赖自定义运算符的情况下完成它。

2 个答案:

答案 0 :(得分:1)

我认为它可以通过内置运算符来解决。下面的代码演示了其中一种方法,但是当用于热源或非轻量级冷源时,事情会变得棘手 - 我鼓励您将其用于教育/获取创意,而不是生产用途

 @Test
fun slidingWindow() {
    val events = Flowable.just(
            Data(5, 1.0),
            Data(10, 1.5),
            Data(30, 2.8),
            Data(40, 3.2),
            Data(60, 3.8),
            Data(90, 4.2))
            .observeOn(Schedulers.io())
    val windows = window(windowSize = 3, slideSize = 1, data = events).toList().blockingGet()
    Assert.assertNotNull(windows)
    Assert.assertFalse(windows.isEmpty())
}

fun window(windowSize: Int, slideSize: Int, data: Flowable<Data>): Flowable<List<Int>> = window(
        from = 0,
        to = windowSize,
        slideSize = slideSize,
        data = data)

fun window(from: Int, to: Int, slideSize: Int, data: Flowable<Data>): Flowable<List<Int>> {
    val window = data.takeWhile { it.time <= to }.skipWhile { it.time < from }.map { it.data }
    val tail = data.skipWhile { it.time <= from + slideSize }
    val nonEmptyWindow = window.toList().filter { !it.isEmpty() }
    val nextWindow = nonEmptyWindow.flatMapPublisher {
        window(from + slideSize, to + slideSize, slideSize, tail).observeOn(Schedulers.io())
    }
    return nonEmptyWindow.toFlowable().concatWith(nextWindow)
}

data class Data(val data: Int, val time: Double)

上述测试得出
 [[5, 10, 30], [10, 30, 40, 60], [30, 40, 60, 90], [40, 60, 90], [90]]

答案 1 :(得分:0)

可以通过操作符timestamp()scan()在扫描内部缓冲元素并确定它们的位置来完成。 使用compose()运算符的示例示例(初始3秒不会缓冲)。

/**
 * Sliding overlapping window. If exactCount of elements are within timespan
 * then groups elements into List otherwise filtered out
 */
fun <T> slidingWindow(
    timespan: Long,
    timespanUnit: TimeUnit,
    exactCount: Int
): (Observable<T>) -> Observable<List<T>> {
    return { from ->
        from.timestamp(timespanUnit)
            .scan(
                ArrayDeque<Timed<T>>(exactCount + 1),
                { queue: ArrayDeque<Timed<T>>, t: Timed<T>
                    ->
                    queue.addLast(t)
                    if (queue.size > exactCount) queue.removeFirst()
                    queue.removeAll { timed -> queue.last().time() - timed.time() > timespan }
                    queue
                }
            )
            .filter { it.size == exactCount }
            .map { it.toTypedArray() }
            .map { it.map { timed -> timed.value() } }
    }
}

    @Test
    fun slidingWindow() {
        val testSequence = "asXZYe".asIterable()
        val timespan = 5000L


        Observable.fromIterable(testSequence)
            .compose(slidingWindow(timespan, TimeUnit.MILLISECONDS, 3))
            .test()
            .assertValues(
                arrayListOf('a', 's', 'X'),
                arrayListOf('s', 'X', 'Z'),
                arrayListOf('X', 'Z', 'Y'),
                arrayListOf('Z', 'Y', 'e')
            )
    }

我的github:github.com/OndrejMalek/RxSlidingWindow