Filtern von Observablen in RxJava

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.