Mathematische und Aggregatoperatoren in RxJava

Mathematische und aggregierte Operatoren in RxJava

1. Einführung

Nach dem Artikel vonintroduction to RxJavawerden wir uns mit aggregierten und mathematischen Operatoren befassen.

These operations must wait for the source Observable to emit all items. Aus diesem Grund ist die Verwendung dieser Operatoren fürObservables gefährlich, die sehr lange oder unendliche Sequenzen darstellen können.

Zweitens verwenden alle Beispiele eine Instanz vonTestSubscriber,, eine bestimmte Vielzahl vonSubscriber, die zum Testen von Einheiten, zum Durchführen von Behauptungen, zum Überprüfen empfangener Ereignisse oder zum Umschließen eines verspottetenSubscriber. verwendet werden können

Betrachten wir nun die mathematischen Operatoren.

2. Konfiguration

Um zusätzliche Operatoren zu verwenden, müssen wiradd the additional dependency bispom.xml: eingeben


    io.reactivex
    rxjava-math
    1.0.0

Oder für ein Gradle-Projekt:

compile 'io.reactivex:rxjava-math:1.0.0'

3. Mathematische Operatoren

The MathObservable is dedicated to performing mathematical operations und seine Operatoren verwenden ein weiteresObservable, das Elemente ausgibt, die als Zahlen ausgewertet werden können.

3.1. Average

Der Operatoraveragegibt einen einzelnen Wert aus - den Durchschnitt aller von der Quelle ausgegebenen Werte.

Lassen Sie uns das in Aktion sehen:

Observable sourceObservable = Observable.range(1, 20);
TestSubscriber subscriber = TestSubscriber.create();

MathObservable.averageInteger(sourceObservable).subscribe(subscriber);

subscriber.assertValue(10);

Es gibt vier ähnliche Operatoren für den Umgang mit primitiven Werten:averageInteger,averageLong,averageFloat undaverageDouble.

3.2. Max

Der Operatormaxgibt die größte angetroffene Zahl aus.

Lassen Sie uns das in Aktion sehen:

Observable sourceObservable = Observable.range(1, 20);
TestSubscriber subscriber = TestSubscriber.create();

MathObservable.max(sourceObservable).subscribe(subscriber);

subscriber.assertValue(9);

Es ist wichtig zu beachten, dass der Operatormaxüber eine überladene Methode verfügt, die eine Vergleichsfunktion übernimmt.

In Anbetracht der Tatsache, dass die mathematischen Operatoren auch mit Objekten arbeiten können, die als Zahlen verwaltet werden können, ermöglicht der überladene Operatormaxden Vergleich benutzerdefinierter Typen oder die benutzerdefinierte Sortierung von Standardtypen.

Definieren wir dieItem-Klasse:

class Item {
    private Integer id;

    // standard constructors, getter, and setter
}

Wir können nun dieitemObservable definieren und dann denmax-Operator verwenden, um dieItem mit den höchstenid auszugeben:

Item five = new Item(5);
List list = Arrays.asList(
  new Item(1),
  new Item(2),
  new Item(3),
  new Item(4),
  five);
Observable itemObservable = Observable.from(list);

TestSubscriber subscriber = TestSubscriber.create();

MathObservable.from(itemObservable)
  .max(Comparator.comparing(Item::getId))
  .subscribe(subscriber);

subscriber.assertValue(five);

3.3. Min

Der Operatormin __ gibt ein einzelnes Element aus, das das kleinste Element aus der Quelle enthält:

Observable sourceObservable = Observable.range(1, 20);
TestSubscriber subscriber = TestSubscriber.create();

MathObservable.min(sourceObservable).subscribe(subscriber);

subscriber.assertValue(1);

Der Operatorminhat eine überladene Methode, die eine Komparatorinstanz akzeptiert:

Item one = new Item(1);
List list = Arrays.asList(
  one,
  new Item(2),
  new Item(3),
  new Item(4),
  new Item(5));
TestSubscriber subscriber = TestSubscriber.create();
Observable itemObservable = Observable.from(list);

MathObservable.from(itemObservable)
  .min(Comparator.comparing(Item::getId))
  .subscribe(subscriber);

subscriber.assertValue(one);

3.4. Sum

Der Operatorsum gibt einen einzelnen Wert aus, der die Summe aller von der QuelleObservable: ausgegebenen Zahlen darstellt

Observable sourceObservable = Observable.range(1, 20);
TestSubscriber subscriber = TestSubscriber.create();

MathObservable.sumInteger(sourceObservable).subscribe(subscriber);

subscriber.assertValue(210);

Es gibt auch primitiv spezialisierte ähnliche Operatoren:sumInteger,sumLong,sumFloat undsumDouble.

4. Aggregatoperatoren

4.1. Concat

Der Operatorconcat verbindet die von der Quelle ausgegebenen Elemente mit.

Definieren wir nun zweiObservablesund verketten sie:

List listOne = Arrays.asList(1, 2, 3, 4);
Observable observableOne = Observable.from(listOne);

List listTwo = Arrays.asList(5, 6, 7, 8);
Observable observableTwo = Observable.from(listTwo);

TestSubscriber subscriber = TestSubscriber.create();

Observable concatObservable = observableOne
  .concatWith(observableTwo);

concatObservable.subscribe(subscriber);

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

Im Detail wartet der Operator vonconcatmit dem Abonnieren aller zusätzlichenObservable, die an ihn übergeben werden, bis der vorherige abgeschlossen ist.

Aus diesem Grund führt die Verkettung eines „heißen“Observable,, der sofort mit der Ausgabe von Elementen beginnt, zum Verlust von Elementen, die der „heiße“Observableausgibt, bevor alle vorherigen abgeschlossen sind.

4.2. Count

Der Operatorcountgibt die Anzahl aller von der Quelle ausgegebenen Elemente aus:

Zählen wir die Anzahl der vonObservable ausgegebenen Elemente:

List lettersList = Arrays.asList(
  "A", "B", "C", "D", "E", "F", "G");
TestSubscriber subscriber = TestSubscriber.create();

Observable sourceObservable = Observable
  .from(lettersList).count();
sourceObservable.subscribe(subscriber);

subscriber.assertValue(7);

Wenn die QuelleObservable mit einem Fehler endet, übergeben diecount einen Benachrichtigungsfehler, ohne ein Element auszugeben. Wenn es jedoch überhaupt nicht beendet wird, geben diecount weder ein Element aus noch werden sie beendet.

Für diecount-Operation gibt es auch dencountLong-Operator, der am Ende einenLong-Wert für diejenigen Sequenzen ausgibt, die die Kapazität vonInteger überschreiten können.

4.3. Reduce

Der Operatorreduce reduziert alle emittierten Elemente durch Anwenden der Akkumulatorfunktion zu einem einzigen Element.

Dieser Vorgang wird fortgesetzt, bis alle Elemente ausgegeben wurden, und dann gibtObservable, vonreduce, den von der Funktion zurückgegebenen Endwert aus.

Lassen Sie uns nun sehen, wie es möglich ist, eine Liste vonStringzu reduzieren und sie in umgekehrter Reihenfolge zu verketten:

List list = Arrays.asList("A", "B", "C", "D", "E", "F", "G");
TestSubscriber subscriber = TestSubscriber.create();

Observable reduceObservable = Observable.from(list)
  .reduce((letter1, letter2) -> letter2 + letter1);
reduceObservable.subscribe(subscriber);

subscriber.assertValue("GFEDCBA");

4.4. Collect

Der Operatorcollectähnelt dem Operatorreduce, dient jedoch zum Sammeln von Elementen in einer einzigen veränderlichen Datenstruktur.

Es erfordert zwei Parameter:

  • Eine Funktion, die die leere veränderbare Datenstruktur zurückgibt

  • Eine Funktion, die bei Angabe der Datenstruktur und eines ausgegebenen Elements die Datenstruktur entsprechend ändert

Mal sehen, wie es möglich sein kann,set von Elementen vonObservable zurückzugeben:

List list = Arrays.asList("A", "B", "C", "B", "B", "A", "D");
TestSubscriber subscriber = TestSubscriber.create();

Observable> reduceListObservable = Observable
  .from(list)
  .collect(HashSet::new, HashSet::add);
reduceListObservable.subscribe(subscriber);

subscriber.assertValues(new HashSet(list));

4.5. ToList

Der OperatortoList funktioniert genauso wie die Operationcollect, sammelt jedoch alle Elemente in einer einzigen Liste. Denken Sie anCollectors.toList() in der Stream-API:

Observable sourceObservable = Observable.range(1, 5);
TestSubscriber subscriber = TestSubscriber.create();

Observable> listObservable = sourceObservable
  .toList();
listObservable.subscribe(subscriber);

subscriber.assertValue(Arrays.asList(1, 2, 3, 4, 5));

4.6. ToSortedList

Genau wie im vorherigen Beispiel, aber die ausgegebene Liste ist sortiert:

Observable sourceObservable = Observable.range(10, 5);
TestSubscriber subscriber = TestSubscriber.create();

Observable> listObservable = sourceObservable
  .toSortedList();
listObservable.subscribe(subscriber);

subscriber.assertValue(Arrays.asList(10, 11, 12, 13, 14));

Wie wir sehen können, verwendettoSortedList den Standardvergleich, es ist jedoch möglich, eine benutzerdefinierte Vergleichsfunktion bereitzustellen. Wir können jetzt sehen, wie es möglich ist, die Ganzzahlen mithilfe einer benutzerdefinierten Sortierfunktion in umgekehrter Reihenfolge zu sortieren:

Observable sourceObservable = Observable.range(10, 5);
TestSubscriber subscriber = TestSubscriber.create();

Observable> listObservable
  = sourceObservable.toSortedList((int1, int2) -> int2 - int1);
listObservable.subscribe(subscriber);

subscriber.assertValue(Arrays.asList(14, 13, 12, 11, 10));

4.7. ToMap

Der OperatortoMap konvertiert die Folge von Elementen, die vonObservable ausgegeben werden, in eine Karte, die von einer bestimmten Tastenfunktion eingegeben wird.

Insbesondere hat der OperatortoMapverschiedene überladene Methoden, die einen, zwei oder drei der folgenden Parameter erfordern:

  1. diekeySelector, die einen Schlüssel aus dem Element erzeugen

  2. dievalueSelector, die aus dem ausgegebenen Element den tatsächlichen Wert erzeugen, der in der Karte gespeichert wird

  3. diemapFactory, die die Sammlung erstellen, die die Elemente enthält

Beginnen wir mit der Definition einer einfachen KlasseBook:

class Book {
    private String title;
    private Integer year;

    // standard constructors, getters, and setters
}

Wir können jetzt sehen, wie es möglich ist, eine Reihe von emittiertenBook-Elementen inMap umzuwandeln, wobei der Buchtitel als Schlüssel und das Jahr als Wert: verwendet werden

Observable bookObservable = Observable.just(
  new Book("The North Water", 2016),
  new Book("Origin", 2017),
  new Book("Sleeping Beauties", 2017)
);
TestSubscriber subscriber = TestSubscriber.create();

Observable> mapObservable = bookObservable
  .toMap(Book::getTitle, Book::getYear, HashMap::new);
mapObservable.subscribe(subscriber);

subscriber.assertValue(new HashMap() {{
  put("The North Water", 2016);
  put("Origin", 2017);
  put("Sleeping Beauties", 2017);
}});

4.8. ToMultiMap

Bei der Zuordnung wird häufig derselbe Schlüssel für viele Werte verwendet. Die Datenstruktur, die einen Schlüssel mehreren Werten zuordnet, wird als Multimap bezeichnet.

Dies kann mit dem OperatortoMultiMap erreicht werden, der die Folge von Elementen, die vonObservable ausgegeben werden, inList konvertiert. Dies ist auch eine Karte, die von einer bestimmten Tastenfunktion eingegeben wird.

Dieser Operator fügt denjenigen des OperatorstoMap einen weiteren Parameter hinzu,collectionFactory.. Mit diesem Parameter kann angegeben werden, in welchem ​​Auflistungstyp der Wert gespeichert werden soll. Mal sehen, wie das geht:

Observable bookObservable = Observable.just(
  new Book("The North Water", 2016),
  new Book("Origin", 2017),
  new Book("Sleeping Beauties", 2017)
);
TestSubscriber subscriber = TestSubscriber.create();

Observable multiMapObservable = bookObservable.toMultimap(
  Book::getYear,
  Book::getTitle,
  () -> new HashMap<>(),
  (key) -> new ArrayList<>()
);
multiMapObservable.subscribe(subscriber);

subscriber.assertValue(new HashMap() {{
    put(2016, Arrays.asList("The North Water"));
    put(2017, Arrays.asList("Origin", "Sleeping Beauties"));
}});

5. Fazit

In diesem Artikel haben wir uns mit den in RxJava verfügbaren mathematischen und aggregierten Operatoren befasst - und natürlich mit einem einfachen Beispiel für deren Verwendung.

Wie immer finden Sie alle Codebeispiele in diesem Artikel inover on Github.