Filtern von Observables in RxJava
1. Einführung
Nach denIntroduction to RxJava werden wir uns die Filteroperatoren ansehen.
Insbesondere konzentrieren wir uns auf das Filtern, Überspringen, Zeitfiltern und einige erweiterte Filtervorgänge.
2. Filtern
Wenn Sie mitObservable arbeiten, ist es manchmal nützlich, nur eine Teilmenge der ausgegebenen Elemente auszuwählen. Zu diesem Zweck wirdRxJava offers various filtering capabilities.
Schauen wir uns die Methodefilteran.
2.1. Der Operatorfilter
Simply put, the filter operator filters an Observable making sure that emitted items match specified condition, die in Form vonPredicate vorliegt.
Mal sehen, wie wir nur die ungeraden Werte aus den ausgegebenen filtern können:
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. Der Operatortake
Beim Filtern mittake führt die Logik zur Emission der erstenn-Elemente, während die verbleibenden Elemente ignoriert werden.
Mal sehen, wie wir diesourceObservable filtern und nur die ersten beiden Elemente ausgeben können:
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. Der OperatortakeWhile
Bei Verwendung vontakeWhile, werden die gefiltertenObservable so lange Elemente ausgeben, bis sie auf ein erstes Element stoßen, das nicht mitPredicate. übereinstimmt
Mal sehen, wie wirtakeWhile verwenden können - mit einer Filterung vonPredicate:
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. Der OperatortakeFirst
Wann immer wir nur das erste Element ausgeben möchten, das einer bestimmten Bedingung entspricht, können wirtakeFirst(). verwenden
Lassen Sie uns einen kurzen Blick darauf werfen, wie wir das erste Element ausgeben können, das größer als 5 ist:
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 undfirstOrDefault Operatoren
Ein ähnliches Verhalten kann mit derfirst API erreicht werden:
Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = sourceObservable.first();
filteredObservable.subscribe(subscriber);
subscriber.assertValue(1);
Wenn wir jedoch einen Standardwert angeben möchten und keine Elemente ausgegeben werden, können wirfirstOrDefault verwenden:
Observable sourceObservable = Observable.empty();
Observable filteredObservable = sourceObservable.firstOrDefault(-1);
filteredObservable.subscribe(subscriber);
subscriber.assertValue(-1);
2.6. Der OperatortakeLast
Wenn wir als nächstes nur die letztenn-Elemente ausgeben möchten, die von einemObservable ausgegeben werden, können wirtakeLast verwenden.
Mal sehen, wie es möglich ist, nur die letzten drei Elemente auszugeben:
Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = sourceObservable.takeLast(3);
filteredObservable.subscribe(subscriber);
subscriber.assertValues(8, 9, 10);
Wir müssen uns daran erinnern, dass dies die Emission eines Elements aus der QuelleObservable verzögert, bis es abgeschlossen ist.
2.7. last undlastOrDefault
Wenn wir nur das letzte Element ausgeben möchten, außertakeLast(1), können wirlast verwenden.
Dies filtert dieObservable und gibt nur das letzte Element aus, wodurch optional eine Filterung vonPredicate überprüft wird:
Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = sourceObservable
.last(i -> i % 2 != 0);
filteredObservable.subscribe(subscriber);
subscriber.assertValue(9);
WennObservable leer ist, können wirlastOrDefault verwenden, das dieObservable filtert, die den Standardwert ausgeben.
Der Standardwert wird auch ausgegeben, wenn der OperatorlastOrDefaultverwendet wird und keine Elemente vorhanden sind, die die Filterbedingung überprüfen:
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 undelementAtOrDefault Operatoren
Mit dem OperatorelementAt können wir ein einzelnes Element auswählen, das von der QuelleObservable ausgegeben wird, und den Index angeben:
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 löst jedochIndexOutOfBoundException aus, wenn der angegebene Index die Anzahl der ausgegebenen Elemente überschreitet.
Um diese Situation zu vermeiden, können SieelementAtOrDefault – verwenden, die einen Standardwert zurückgeben, falls der Index außerhalb des Bereichs liegt:
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. Der OperatorofType
Immer wennObservableObject ausgibt, ist es möglich, sie nach ihrem Typ zu filtern.
Mal sehen, wie wir nur die vom TypStringausgegebenen Elemente filtern können:
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. Überspringen
Wenn wir andererseits einige der Elemente herausfiltern oder überspringen möchten, die vonObservable,RxJava offers a few operators as a counterpart of the filtering ones ausgegeben werden, die wir zuvor besprochen haben.
Betrachten wir zunächst den Operatorskip, das Gegenstück zutake.
3.1. Der Operatorskip
Wenn einObservable eine Folge von Elementen ausgibt, ist es möglich, einige der zuerst ausgegebenen Elemente mitskip herauszufiltern oder zu überspringen.
Zum Beispiel. Mal sehen, wie es möglich ist, die ersten vier Elemente zu überspringen:
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. Der OperatorskipWhile
Wann immer wir alle ersten Werte herausfiltern möchten, die vonObservable ausgegeben werden und ein Filterprädikat nicht bestehen, können wir den OperatorskipWhile verwenden:
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 Operator
Mit dem OperatorskipLastkönnen wir die endgültigen Elemente überspringen, die vonObservableausgegeben werden, und nur die vor ihnen ausgegebenen Elemente akzeptieren.
Damit können wir zum Beispiel die letzten fünf Punkte überspringen:
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 unddistinctUntilChanged Operatoren
Der Operatordistinct gibtObservable zurück, das alle von densourceObservable ausgegebenen Elemente ausgibt, die unterschiedlich sind:
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);
Wenn wir jedoch einObservable erhalten möchten, das alle von densourceObservable ausgegebenen Elemente ausgibt, die sich von ihrem unmittelbaren Vorgänger unterscheiden, können wir den OperatordistinctUntilChanged verwenden:
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. Der OperatorignoreElements
Wann immer wir alle vonsourceObservable emittierten Elemente ignorieren möchten, können wir einfachignoreElements: verwenden
Observable sourceObservable = Observable.range(1, 10);
TestSubscriber subscriber = new TestSubscriber();
Observable ignoredObservable = sourceObservable.ignoreElements();
ignoredObservable.subscribe(subscriber);
subscriber.assertNoValues();
4. Zeitfilteroperatoren
Wenn Sie mit einer beobachtbaren Sequenz arbeiten, ist die Zeitachse unbekannt. Manchmal kann es jedoch hilfreich sein, aktuelle Daten aus einer Sequenz abzurufen.
Zu diesem Zweck wirdRxJava offers a few methods that allow us to work with Observable using also the time axis.
Bevor wir mit dem ersten fortfahren, definieren wir ein zeitgesteuertesObservable, das jede Sekunde ein Element ausgibt:
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 in dem von uns bevorzugten Tempo.
4.1. sample undthrottleLast Operatoren
Der Operatorsample filterttimedObservable und gibtObservable zurück, das die neuesten von dieser API ausgegebenen Elemente innerhalb von Zeitintervallen ausgibt.
Mal sehen, wie wir dietimedObservable abtasten können, indem wir alle 2,5 Sekunden nur das zuletzt emittierte Element filtern:
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);
Diese Art von Verhalten kann auch mit dem OperatorthrottleLasterreicht werden.
4.2. Der OperatorthrottleFirst
Der OperatorthrottleFirst unterscheidet sich vonthrottleLast/sample, da er das erste vontimedObservable in jeder Abtastperiode emittierte Element anstelle des zuletzt emittierten Elements ausgibt.
Lassen Sie uns sehen, wie wir die ersten Elemente mit einer Abtastperiode von 4 Sekunden ausgeben können:
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 undthrottleWithTimeout Operatoren
Mit dem Operatordebounce ist es möglich, nur ein Element auszugeben, wenn eine bestimmte Zeitspanne verstrichen ist, ohne ein anderes Element auszugeben.
Wenn wir daher eine Zeitspanne auswählen, die größer als das Zeitintervall zwischen den ausgegebenen Elementen dertimedObservable ist, wird nur die letzte. ausgegeben. Wenn sie dagegen kleiner ist, wird sie ausgegeben alle vontimedObservable. ausgegebenen Elemente
Mal sehen, was im ersten Szenario passiert:
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = timedObservable
.debounce(2000L, TimeUnit.MILLISECONDS, testScheduler);
filteredObservable.subscribe(subscriber);
testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);
subscriber.assertValue(6);
Diese Art von Verhalten kann auch mitthrottleWithTimeout erreicht werden.
4.4. Der Operatortimeout
Der Operatortimeout spiegelt die QuelleObservable wider, gibt jedoch einen Benachrichtigungsfehler aus, der die Ausgabe von Elementen abbricht, wenn die QuelleObservable während eines bestimmten Zeitintervalls keine Elemente ausgibt.
Mal sehen, was passiert, wenn wir ein Zeitlimit von 500 Millisekunden für unseretimedObservable angeben:
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. Mehrfach beobachtbare Filterung
Wenn Sie mitObservable arbeiten, können Sie definitiv entscheiden, ob Elemente basierend auf einem zweitenObservable gefiltert oder übersprungen werden sollen.
Bevor wir fortfahren, definieren wir eindelayedObservable, das nach 3 Sekunden nur 1 Element ausgibt:
Observable delayedObservable = Observable.just(1)
.delay(3, TimeUnit.SECONDS, testScheduler);
Beginnen wir mit dem OperatortakeUntil.
5.1. Der OperatortakeUntil
Der OperatortakeUntil verwirft alle von der QuelleObservable (timedObservable) ausgegebenen Elemente, nachdem eine SekundeObservable (delayedObservable) ein Element ausgegeben hat oder beendet:
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = timedObservable
.skipUntil(delayedObservable);
filteredObservable.subscribe(subscriber);
testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);
subscriber.assertValues(4, 5, 6);
5.2. Der OperatorskipUntil
Andererseits verwirftskipUntil jedes von der QuelleObservable (timedObservable) emittierte Element, bis ein zweitesObservable (delayedObservable) ein Element ausgibt:
TestSubscriber subscriber = new TestSubscriber();
Observable filteredObservable = timedObservable
.takeUntil(delayedObservable);
filteredObservable.subscribe(subscriber);
testScheduler.advanceTimeBy(7, TimeUnit.SECONDS);
subscriber.assertValues(1, 2, 3);
6. Fazit
In diesem umfangreichen Lernprogramm haben wir die verschiedenen in RxJava verfügbaren Filteroperatoren untersucht und jeweils ein einfaches Beispiel bereitgestellt.
Wie immer finden Sie alle Codebeispiele in diesem Artikel inover on GitHub.