RxJavaでの観測量のフィルタリング

1前書き

/rx-java[RxJavaの紹介]のリンクをたどって、フィルタリング演算子について見ていきます。

特に、フィルタリング、スキップ、時間フィルタリング、そしてより高度なフィルタリング操作に焦点を当てます。

2フィルタリング

Observable を扱うときは、発行済みアイテムのサブセットのみを選択すると便利なことがあります。この目的のために、 RxJavaはさまざまなフィルタリング機能 を提供します。

filter メソッドを見てみましょう。

2.1. フィルタ 演算子

  • 簡単に言うと、 filter 演算子は Observable をフィルタリングして、発行されたアイテムが Predicate の形式で指定された条件に一致することを確認します。

出力された値から奇数値のみをフィルタリングする方法を見てみましょう。

Observable<Integer> sourceObservable = Observable.range(1, 10);
TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable = sourceObservable
  .filter(i -> i % 2 != 0);

filteredObservable.subscribe(subscriber);

subscriber.assertValues(1, 3, 5, 7, 9);

2.2. take 演算子

  • take でフィルタリングすると、残りの項目を無視しながら最初の n 項目が発行されます。

sourceObservable をフィルタ処理して最初の2つの項目のみを出力する方法を見てみましょう。

Observable<Integer> sourceObservable = Observable.range(1, 10);
TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable = sourceObservable.take(3);

filteredObservable.subscribe(subscriber);

subscriber.assertValues(1, 2, 3);

2.3. takeWhile 演算子

  • takeWhileを使用すると、 Predicateと一致しない最初の要素に遭遇するまで、フィルタリングされた Observable はアイテムを出力し続けます

フィルタリング Predicateを使って takeWhile をどのように使用できるかを見てみましょう:

Observable<Integer> sourceObservable = Observable.just(1, 2, 3, 4, 3, 2, 1);
TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable = sourceObservable
  .takeWhile(i -> i < 4);

filteredObservable.subscribe(subscriber);

subscriber.assertValues(1, 2, 3);

2.4. takeFirst 演算子

  • 与えられた条件にマッチする最初のアイテムだけを発行したいときはいつでも、__takeFirst()を使うことができます。

5を超える最初の項目をどのようにして発行できるかを簡単に見てみましょう。

Observable<Integer> sourceObservable = Observable
  .just(1, 2, 3, 4, 5, 7, 6);
TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable = sourceObservable
  .takeFirst(x -> x > 5);

filteredObservable.subscribe(subscriber);

subscriber.assertValue(7);

2.5. first firstOrDefault 演算子

first APIを使用しても同様の動作を実現できます。

Observable<Integer> sourceObservable = Observable.range(1, 10);
TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable = sourceObservable.first();

filteredObservable.subscribe(subscriber);

subscriber.assertValue(1);
  • しかし、デフォルト値を指定したい場合、アイテムが出力されなければ、 firstOrDefault を使用できます。

Observable<Integer> sourceObservable = Observable.empty();

Observable<Integer> filteredObservable = sourceObservable.firstOrDefault(-1);

filteredObservable.subscribe(subscriber);

subscriber.assertValue(-1);

2.6. takeLast 演算子

次に、 Observable によって最後に放出された n 個のアイテムだけを放出したい場合は、 takeLast を使用できます。

最後の3つの項目だけを発行することがどのように可能であるかを見てみましょう:

Observable<Integer> sourceObservable = Observable.range(1, 10);
TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable = sourceObservable.takeLast(3);

filteredObservable.subscribe(subscriber);

subscriber.assertValues(8, 9, 10);

これが完了するまで、ソースObservableからのアイテムの発行を遅らせることを忘れないでください。

2.7. last lastOrDefault

  • 最後の要素だけを放出したい場合は、 takeLast(1) を使用する以外に、 last を使用できます。

これは Observable をフィルタリングし、最後の要素のみを出力します。これはオプションでフィルタリング Predicate を検証します。

Observable<Integer> sourceObservable = Observable.range(1, 10);
TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable = sourceObservable
  .last(i -> i % 2 != 0);

filteredObservable.subscribe(subscriber);

subscriber.assertValue(9);

Observable が空の場合、 lastOrDefault を使用して、 Observable をフィルタリングしてデフォルト値を出力することができます。

lastOrDefault 演算子が使用されていて、フィルタ条件を検証する項目がない場合も、デフォルト値が生成されます。

Observable<Integer> sourceObservable = Observable.range(1, 10);
TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable =
  sourceObservable.lastOrDefault(-1, i -> i > 10);

filteredObservable.subscribe(subscriber);

subscriber.assertValue(-1);

2.8. elementAt および elementAtOrDefault 演算子

elementAt 演算子を使用すると、インデックスを指定して、ソース Observable によって発行された単一のアイテムを選択できます。

Observable<Integer> sourceObservable = Observable
  .just(1, 2, 3, 5, 7, 11);
TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable = sourceObservable.elementAt(4);

filteredObservable.subscribe(subscriber);

subscriber.assertValue(7);

ただし、指定されたインデックスが発行されたアイテムの数を超えると、 elementAt IndexOutOfBoundException をスローします。

このような状況を避けるために、インデックスが範囲外の場合にデフォルト値を返す elementAtOrDefault – を使用することが可能です。

Observable<Integer> sourceObservable = Observable
  .just(1, 2, 3, 5, 7, 11);
TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable
 = sourceObservable.elementAtOrDefault(7, -1);

filteredObservable.subscribe(subscriber);

subscriber.assertValue(-1);

2.9. ofType 演算子

  • Observable Object アイテムを発行するときはいつでも、それらのタイプに基づいてそれらをフィルタリングすることが可能です。**

どのようにして String タイプのアイテムを出力することができるのかを見てみましょう。

Observable sourceObservable = Observable.just(1, "two", 3, "five", 7, 11);
TestSubscriber subscriber = new TestSubscriber();

Observable filteredObservable = sourceObservable.ofType(String.class);

filteredObservable.subscribe(subscriber);

subscriber.assertValues("two", "five");

3スキップする

一方、 Observable によって放出された項目のいくつかを除外またはスキップしたい場合、 RxJavaは先ほど説明したように、フィルタリング の対応物としていくつかの演算子を提供します。

take の対応する skip 演算子を見てみましょう。

3.1. skip 演算子

Observable が一連のアイテムを発行するとき、 skip を使用して最初に発行されたアイテムの一部を除外またはスキップすることができます。

例えば。最初の4つの要素をスキップすることができる方法を見てみましょう。

Observable<Integer> sourceObservable = Observable.range(1, 10);
TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable = sourceObservable.skip(4);

filteredObservable.subscribe(subscriber);

subscriber.assertValues(5, 6, 7, 8, 9, 10);

3.2. skipWhile 演算子

  • Observable から出力された、フィルタリング述語に合格しなかった最初の値をすべて除外したいときはいつでも、 skipWhile__演算子を使用できます。

Observable<Integer> sourceObservable = Observable
  .just(1, 2, 3, 4, 5, 4, 3, 2, 1);
TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable = sourceObservable
  .skipWhile(i -> i < 4);

filteredObservable.subscribe(subscriber);

subscriber.assertValues(4, 5, 4, 3, 2, 1);

3.3. skipLast 演算子

skipLast 演算子を使用すると、 Observable によって発行された最後のアイテムをスキップして、それらの前に発行されたアイテムのみを受け入れることができます。

これにより、たとえば、最後の5つの項目をスキップできます。

Observable<Integer> sourceObservable = Observable.range(1, 10);
TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable = sourceObservable.skipLast(5);

filteredObservable.subscribe(subscriber);

subscriber.assertValues(1, 2, 3, 4, 5);

3.4. distinct distinctUntilChanged 演算子

  • distinct 演算子は、 sourceObservable によって発行された次のように異なるすべてのアイテムを発行する Observable を返します。

Observable<Integer> sourceObservable = Observable
  .just(1, 1, 2, 2, 1, 3, 3, 1);
TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> distinctObservable = sourceObservable.distinct();

distinctObservable.subscribe(subscriber);

subscriber.assertValues(1, 2, 3);

ただし、 sourceObservable によって発行された、直前のものとは異なるすべてのアイテムを発行する Observable を取得する場合は、 distinctUntilChanged 演算子を使用できます。

Observable<Integer> sourceObservable = Observable
  .just(1, 1, 2, 2, 1, 3, 3, 1);
TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> distinctObservable = sourceObservable.distinctUntilChanged();

distinctObservable.subscribe(subscriber);

subscriber.assertValues(1, 2, 1, 3, 1);

3.5. ignoreElements 演算子

sourceObservable によって発行されたすべての要素を無視したい場合はいつでも、単に__ignoreElementsを使用できます。

Observable<Integer> sourceObservable = Observable.range(1, 10);
TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> ignoredObservable = sourceObservable.ignoreElements();

ignoredObservable.subscribe(subscriber);

subscriber.assertNoValues();

4時間フィルタリング演算子

観測可能なシーケンスを扱う場合、時間軸は不明ですが、シーケンスからタイムリーなデータを取得することが役に立つ場合があります。

この目的のために、** RxJavaは、時間軸を使ってもObservable__を扱うことができるようにするいくつかの方法を提供します。

最初のものに移る前に、毎秒アイテムを発行する時間指定のObservable__を定義しましょう。

TestScheduler testScheduler = new TestScheduler();

Observable<Integer> timedObservable = Observable
  .just(1, 2, 3, 4, 5, 6)
  .zipWith(Observable.interval(
    0, 1, TimeUnit.SECONDS, testScheduler), (item, time) -> item);
  • TestScheduler は、** 私たちが好む速度で手動で時計を進めることを可能にする特別なスケジューラです。

4.1. sample および throttleLast 演算子

sample 演算子は timedObservable をフィルタリングし、期間内にこのAPIによって発行された最新のアイテムを発行する Observable を返します。

2.5秒ごとに最後に発行されたアイテムのみをフィルタリングして、 timedObservable をサンプリングする方法を見てみましょう。

TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> sampledObservable = timedObservable
  .sample(2500L, TimeUnit.MILLISECONDS, testScheduler);

sampledObservable.subscribe(subscriber);

testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);

subscriber.assertValues(3, 5, 6);

この種の動作は、 throttleLast 演算子を使用しても実現できます。

4.2. throttleFirst 演算子

throttleFirst 演算子は、最後に発行されたものではなく、各サンプリング期間内に timedObservable によって発行された最初の項目を発行するため、 throttleLast/sample とは異なります。

4秒のサンプリング期間を使用して、最初のアイテムをどのように放出できるかを見てみましょう。

TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable = timedObservable
  .throttleFirst(4100L, TimeUnit.SECONDS, testScheduler);

filteredObservable.subscribe(subscriber);

testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);

subscriber.assertValues(1, 6);

4.3. debounce および throttleWithTimeout 演算子

debounce 演算子を使用すると、特定の期間が経過した場合に別のアイテムを送信せずにアイテムのみを送信できます。

したがって、 timedObservable の発行済みアイテム間の時間間隔よりも長いタイムスパンを選択した場合、最後のアイテムのみが発行されます。 .__

最初のシナリオで何が起こるか見てみましょう。

TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable = timedObservable
  .debounce(2000L, TimeUnit.MILLISECONDS, testScheduler);

filteredObservable.subscribe(subscriber);

testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);

subscriber.assertValue(6);

この種の動作は throttleWithTimeout を使っても達成できます。

4.4. タイムアウト 演算子

timeout 演算子はソースの Observable をミラーリングしますが、指定された時間内にソースの Observable がアイテムの発行に失敗すると、通知エラーを発行してアイテムの発行を中止します。

timedObservable に500ミリ秒のタイムアウトを指定した場合にどうなるかを見てみましょう。

TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable = timedObservable
  .timeout(500L, TimeUnit.MILLISECONDS, testScheduler);

filteredObservable.subscribe(subscriber);

testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);

subscriber.assertError(TimeoutException.class); subscriber.assertValues(1);

5多重観測可能フィルタリング

Observable を使用しているときは、2番目の Observable に基づいてアイテムをフィルタ処理するかスキップするかを決定することは間違いなく可能です。

先に進む前に、3秒後に1つのアイテムのみを放出する delayedObservable を定義しましょう。

Observable<Integer> delayedObservable = Observable.just(1)
  .delay(3, TimeUnit.SECONDS, testScheduler);

takeUntil 演算子から始めましょう。

5.1. takeUntil 演算子

takeUntil 演算子は、2番目の Observable delayedObservable )がアイテムを発行した後でソース Observable timedObservable )が発行したアイテムを破棄するか、終了します。

TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable = timedObservable
  .skipUntil(delayedObservable);

filteredObservable.subscribe(subscriber);

testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);

subscriber.assertValues(4, 5, 6);

5.2. skipUntil 演算子

一方、 skipUntil は、2番目の Observable delayedObservable )が項目を発行するまで、ソース Observable timedObservable )が発行した項目をすべて破棄します。

TestSubscriber<Integer> subscriber = new TestSubscriber();

Observable<Integer> filteredObservable = timedObservable
  .takeUntil(delayedObservable);

filteredObservable.subscribe(subscriber);

testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);

subscriber.assertValues(1, 2, 3);

6. 結論

この広範なチュートリアルでは、RxJava内で利用可能なさまざまなフィルタリング演算子を探り、それぞれの簡単な例を示しました。

いつものように、この記事のすべてのコード例はhttps://github.com/eugenp/tutorials/tree/master/rxjava[over on Github]にあります。