RxJavaでオブザーバブルをフィルタリングする
1. 前書き
Introduction to RxJavaに続いて、フィルタリング演算子を見ていきます。
特に、フィルタリング、スキップ、時間フィルタリング、およびいくつかのより高度なフィルタリング操作に焦点を当てます。
2. フィルタリング
Observableを使用する場合、放出されたアイテムのサブセットのみを選択すると便利な場合があります。 この目的のために、RxJava offers various filtering capabilities。
filterメソッドを見てみましょう。
2.1. filter演算子
Simply put, the filter operator filters an Observable making sure that emitted items match specified condition。これはPredicateの形式で提供されます。
放出された値から奇数値のみをフィルタリングする方法を見てみましょう。
Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();
Observable 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 sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = sourceObservable.take(3);
filteredObservable.subscribe(subscriber);
subscriber.assertValues(1, 2, 3);
2.3. takeWhile演算子
takeWhile,を使用する場合、フィルタリングされたObservableは、Predicate.と一致しない最初の要素に遭遇するまでアイテムを放出し続けます
Predicate:をフィルタリングしてtakeWhileを使用する方法を見てみましょう
Observable sourceObservable = Observable.just(1, 2, 3, 4, 3, 2, 1);
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = sourceObservable
.takeWhile(i -> i < 4);
filteredObservable.subscribe(subscriber);
subscriber.assertValues(1, 2, 3);
2.4. takeFirst演算子
特定の条件に一致する最初のアイテムのみを発行する場合は常に、takeFirst().を使用できます。
5より大きい最初のアイテムを発行する方法を簡単に見てみましょう。
Observable sourceObservable = Observable
.just(1, 2, 3, 4, 5, 7, 6);
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = sourceObservable
.takeFirst(x -> x > 5);
filteredObservable.subscribe(subscriber);
subscriber.assertValue(7);
2.5. firstおよびfirstOrDefault演算子
同様の動作は、firstAPIを使用して実現できます。
Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = sourceObservable.first();
filteredObservable.subscribe(subscriber);
subscriber.assertValue(1);
ただし、デフォルト値を指定する場合、アイテムが発行されない場合は、firstOrDefaultを使用できます。
Observable sourceObservable = Observable.empty();
Observable filteredObservable = sourceObservable.firstOrDefault(-1);
filteredObservable.subscribe(subscriber);
subscriber.assertValue(-1);
2.6. takeLast演算子
次に、Observableによって発行された最後のnアイテムのみを発行する場合は、takeLastを使用できます。
最後の3つのアイテムのみを放出する方法を見てみましょう。
Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = sourceObservable.takeLast(3);
filteredObservable.subscribe(subscriber);
subscriber.assertValues(8, 9, 10);
これにより、ソースObservableからのアイテムの放出が完了するまで遅延することを覚えておく必要があります。
2.7. lastおよびlastOrDefault
最後の要素のみを出力する場合は、takeLast(1)を使用する以外に、lastを使用できます。
これにより、Observableがフィルタリングされ、最後の要素のみが出力されます。これにより、オプションでフィルタリングPredicateが検証されます。
Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = sourceObservable
.last(i -> i % 2 != 0);
filteredObservable.subscribe(subscriber);
subscriber.assertValue(9);
Observableが空の場合、lastOrDefaultを使用して、デフォルト値を出力するObservableをフィルタリングできます。
lastOrDefault演算子が使用され、フィルタリング条件を検証する項目がない場合にも、デフォルト値が発行されます。
Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable =
sourceObservable.lastOrDefault(-1, i -> i > 10);
filteredObservable.subscribe(subscriber);
subscriber.assertValue(-1);
2.8. elementAtおよびelementAtOrDefault演算子
elementAt演算子を使用すると、ソースObservableによって発行された単一のアイテムを選択し、そのインデックスを指定できます。
Observable sourceObservable = Observable
.just(1, 2, 3, 5, 7, 11);
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = sourceObservable.elementAt(4);
filteredObservable.subscribe(subscriber);
subscriber.assertValue(7);
ただし、指定されたインデックスが放出されたアイテムの数を超える場合、elementAtはIndexOutOfBoundExceptionをスローします。
この状況を回避するために、elementAtOrDefault –を使用して、インデックスが範囲外の場合にデフォルト値を返すことができます。
Observable sourceObservable = Observable
.just(1, 2, 3, 5, 7, 11);
TestSubscriber subscriber = new TestSubscriber();
Observable 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 offers a few operators as a counterpart of the filtering onesによって発行されたアイテムの一部を除外またはスキップする場合。
takeに対応するskip演算子を見てみましょう。
3.1. skip演算子
Observableが一連のアイテムを放出する場合、skipを使用して、最初に放出されたアイテムの一部を除外またはスキップすることができます。
例えば。 最初の4つの要素をスキップする方法を見てみましょう。
Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = sourceObservable.skip(4);
filteredObservable.subscribe(subscriber);
subscriber.assertValues(5, 6, 7, 8, 9, 10);
3.2. skipWhile演算子
フィルタリング述語に失敗するObservableによって発行された最初の値をすべて除外する場合は常に、skipWhile演算子を使用できます。
Observable sourceObservable = Observable
.just(1, 2, 3, 4, 5, 4, 3, 2, 1);
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = sourceObservable
.skipWhile(i -> i < 4);
filteredObservable.subscribe(subscriber);
subscriber.assertValues(4, 5, 4, 3, 2, 1);
3.3. TheskipLast演算子
skipLast演算子を使用すると、Observableによって発行された最後のアイテムをスキップして、前に発行されたアイテムのみを受け入れることができます。
これにより、たとえば、最後の5つの項目をスキップできます。
Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = sourceObservable.skipLast(5);
filteredObservable.subscribe(subscriber);
subscriber.assertValues(1, 2, 3, 4, 5);
3.4. distinctおよびdistinctUntilChanged演算子
distinct演算子は、sourceObservableによって発行されたすべてのアイテムを発行するObservableを返します。
Observable sourceObservable = Observable
.just(1, 1, 2, 2, 1, 3, 3, 1);
TestSubscriber subscriber = new TestSubscriber();
Observable distinctObservable = sourceObservable.distinct();
distinctObservable.subscribe(subscriber);
subscriber.assertValues(1, 2, 3);
ただし、直前の先行オブジェクトとは異なるsourceObservableによって発行されたすべてのアイテムを発行するObservableを取得する場合は、distinctUntilChanged演算子を使用できます。
Observable sourceObservable = Observable
.just(1, 1, 2, 2, 1, 3, 3, 1);
TestSubscriber subscriber = new TestSubscriber();
Observable distinctObservable = sourceObservable.distinctUntilChanged();
distinctObservable.subscribe(subscriber);
subscriber.assertValues(1, 2, 1, 3, 1);
3.5. ignoreElements演算子
sourceObservableによって放出されたすべての要素を無視したいときはいつでも、単にignoreElements:を使用できます。
Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();
Observable ignoredObservable = sourceObservable.ignoreElements();
ignoredObservable.subscribe(subscriber);
subscriber.assertNoValues();
4. 時間フィルタリング演算子
観測可能なシーケンスを使用する場合、時間軸は不明ですが、シーケンスからタイムリーなデータを取得することが役立つ場合があります。
この目的で、RxJava offers a few methods that allow us to work with Observable using also the time axis。
最初のものに移る前に、毎秒アイテムを放出する時限Observableを定義しましょう:
TestScheduler testScheduler = new TestScheduler();
Observable timedObservable = Observable
.just(1, 2, 3, 4, 5, 6)
.zipWith(Observable.interval(
0, 1, TimeUnit.SECONDS, testScheduler), (item, time) -> item);
The TestScheduler is a special scheduler that allows advancing the clock manuallyは任意のペースで。
4.1. sampleおよびthrottleLast演算子
sample演算子は、timedObservableをフィルタリングし、期間の時間間隔内にこのAPIによって発行された最新のアイテムを発行するObservableを返します。
2.5秒ごとに最後に放出されたアイテムのみをフィルタリングして、timedObservableをサンプリングする方法を見てみましょう。
TestSubscriber subscriber = new TestSubscriber();
Observable 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 subscriber = new TestSubscriber();
Observable 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の放出されたアイテム間の時間間隔よりも大きいタイムスパンを選択すると、最後の1.のみが放出されます。一方、それよりも小さい場合は、放出されます。 timedObservable.によって放出されたすべてのアイテム
最初のシナリオで何が起こるか見てみましょう。
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = timedObservable
.debounce(2000L, TimeUnit.MILLISECONDS, testScheduler);
filteredObservable.subscribe(subscriber);
testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);
subscriber.assertValue(6);
この種の動作は、throttleWithTimeoutを使用して実現することもできます。
4.4. timeout演算子
timeout演算子は、ソースObservableをミラーリングしますが、ソースObservableが指定された時間間隔内にアイテムの発行に失敗した場合、通知エラーを発行してアイテムの発行を中止します。
timedObservableに500ミリ秒のタイムアウトを指定するとどうなるか見てみましょう。
TestSubscriber subscriber = new TestSubscriber();
Observable 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に基づいてアイテムをフィルタリングするかスキップするかを決定することは間違いなく可能です。
先に進む前に、delayedObservableを定義しましょう。これは、3秒後に1つのアイテムのみを放出します。
Observable delayedObservable = Observable.just(1)
.delay(3, TimeUnit.SECONDS, testScheduler);
takeUntil演算子から始めましょう。
5.1. takeUntil演算子
takeUntil演算子は、2番目のObservable(delayedObservable)がアイテムを放出した後、または終了した後、ソースObservable(timedObservable)によって放出されたアイテムを破棄します。
TestSubscriber subscriber = new TestSubscriber();
Observable 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 subscriber = new TestSubscriber();
Observable filteredObservable = timedObservable
.takeUntil(delayedObservable);
filteredObservable.subscribe(subscriber);
testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);
subscriber.assertValues(1, 2, 3);
6. 結論
この広範なチュートリアルでは、RxJava内で使用可能なさまざまなフィルタリング演算子を調べ、それぞれの簡単な例を示します。
いつものように、この記事のすべてのコード例はover on GitHubにあります。