RxJava-将Observable转换为Iterator,Stream或Sequence

时间:2016-05-24 18:57:19

标签: stream sequence rx-java kotlin rx-kotlin

我知道这会破坏很多Rx规则,但我真的很喜欢RxJava-JDBC我的队友也是如此。关系数据库是我们工作的核心,Rx也是如此。

然而,在某些情况下,我们不希望以Observable<ResultSet>的形式发射,而是仅仅拥有基于拉取的Java 8 Stream<ResultSet>或Kotlin Sequence<ResultSet>。但我们已经习惯了只返回Observable<ResultSet>的RxJava-JDBC库。

因此,我想知道是否有办法可以使用扩展功能将Observable<ResultSet>转换为Sequence<ResultSet>,而不是进行任何中间收集或toBlocking()调用。下面是我到目前为止的所有内容,但我现在正在尝试连接基于推送和拉取的系统,我无法缓冲,因为每个ResultSet调用onNext()都是有状态的。这是一项不可能完成的任务吗?

import rx.Observable
import rx.Subscriber
import java.sql.ResultSet

fun Observable<ResultSet>.asSequence() = object: Iterator<ResultSet>, Subscriber<ResultSet>() {

    private var isComplete = false

    override fun onCompleted() {
        isComplete = true
    }

    override fun onError(e: Throwable?) {
        throw UnsupportedOperationException()
    }

    override fun onNext(rs: ResultSet?) {
        throw UnsupportedOperationException()
    }


    override fun hasNext(): Boolean {
        throw UnsupportedOperationException()
    }

    override fun next(): ResultSet {
        throw UnsupportedOperationException()
    }

}.asSequence()

2 个答案:

答案 0 :(得分:4)

我不确定这是实现您想要的最简单方法,但您可以尝试使用此代码。它通过创建阻止队列并将Observable中的所有事件发布到此队列,将Iterator转换为ObservableIterable从队列中提取事件,如果没有,则阻止。然后根据收到的当前事件修改自己的状态。

class ObservableIterator<T>(
    observable: Observable<T>,
    scheduler: Scheduler
) : Iterator<T>, Closeable {

  private val queue = LinkedBlockingQueue<Notification<T>>()
  private var cached: Notification<T>? = null
  private var completed: Boolean = false

  private val subscription =
      observable
          .materialize()
          .subscribeOn(scheduler)
          .subscribe({ queue.put(it) })

  override fun hasNext(): Boolean {
    cacheNext()
    return !completed
  }

  override fun next(): T {
    cacheNext()
    val notification = cached ?: throw NoSuchElementException()
    check(notification.isOnNext)
    cached = null
    return notification.value
  }

  private fun cacheNext() {
    if (completed) {
      return
    }

    if (cached == null) {
      queue.take().let { notification ->
        if (notification.isOnError) {
          completed = true
          throw RuntimeException(notification.throwable)
        } else if (notification.isOnCompleted) {
          completed = true
        } else {
          cached = notification
        }
      }
    }
  }

  override fun close() {
    subscription.unsubscribe()
    completed = true
    cached = null
  }
}

答案 1 :(得分:2)

您可以使用以下辅助函数:

fun <T> Observable<T>.asSequence() = Sequence { toBlocking().getIterator() }

当为迭代器调用返回的序列时,将订阅observable。

如果一个observable在它所订阅的同一个线程上发出元素(比如Observable.just),它将在它有机会被返回之前填充迭代器的缓冲区。 在这种情况下,您可能需要通过调用subscribeOn来直接订阅不同的线程:

observable.subscribeOn(scheduler).asSequence()

然而,虽然toBlocking().getIterator()没有缓冲所有结果,但如果它们没有被迭代器及时消耗,它可以缓冲其中一些结果。如果ResultSet在下一个ResultSet到达时以某种方式过期,则可能会出现问题。