Opérateurs mathématiques et agrégés dans RxJava

Opérateurs Mathématiques et Agrégats dans RxJava

1. introduction

Suite à l'article deintroduction to RxJava, nous allons examiner les opérateurs agrégés et mathématiques.

These operations must wait for the source Observable to emit all items. Pour cette raison, ces opérateurs sont dangereux à utiliser surObservables qui peuvent représenter des séquences très longues ou infinies.

Deuxièmement, tous les exemples utilisent une instance desTestSubscriber, une variété particulière deSubscriber qui peut être utilisée pour les tests unitaires, pour effectuer des assertions, inspecter les événements reçus ou envelopper unSubscriber. simulé

Commençons maintenant par examiner les opérateurs mathématiques.

2. Installer

Pour utiliser des opérateurs supplémentaires, nous aurons besoin deadd the additional dependency auxpom.xml:


    io.reactivex
    rxjava-math
    1.0.0

Ou, pour un projet Gradle:

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

3. Opérateurs mathématiques

The MathObservable is dedicated to performing mathematical operations et ses opérateurs utilisent un autreObservable qui émet des éléments qui peuvent être évalués sous forme de nombres.

3.1. Average

L'opérateuraverage émet une valeur unique - la moyenne de toutes les valeurs émises par la source.

Voyons cela en action:

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

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

subscriber.assertValue(10);

Il existe quatre opérateurs similaires pour traiter les valeurs primitives:averageInteger,averageLong,averageFloat etaverageDouble.

3.2. Max

L'opérateurmax émet le plus grand nombre rencontré.

Voyons cela en action:

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

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

subscriber.assertValue(9);

Il est important de noter que l’opérateurmax a une méthode surchargée qui prend une fonction de comparaison.

Compte tenu du fait que les opérateurs mathématiques peuvent également travailler sur des objets qui peuvent être gérés comme des nombres, l'opérateur surchargémax permet de comparer des types personnalisés ou un tri personnalisé des types standard.

Définissons la classeItem:

class Item {
    private Integer id;

    // standard constructors, getter, and setter
}

On peut maintenant définir lesitemObservable puis utiliser l'opérateurmax pour émettre lesItem avec lesid les plus élevés:

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

L'opérateurmin __ émet un seul élément contenant le plus petit élément de la source:

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

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

subscriber.assertValue(1);

L'opérateurmin a une méthode surchargée qui accepte une instance de comparateur:

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

L'opérateursum émet une valeur unique qui représente la somme de tous les nombres émis par la sourceObservable:

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

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

subscriber.assertValue(210);

Il existe également des opérateurs similaires spécialisés primitifs:sumInteger,sumLong,sumFloat etsumDouble.

4. Opérateurs d'agrégats

4.1. Concat

L'opérateurconcat joint les éléments émis par la source entre eux.

Définissons maintenant deuxObservables et concaténons-les:

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);

Pour entrer dans les détails, l'opérateurconcat attend avec l'abonnement à chaqueObservable supplémentaire qui lui est transmis jusqu'à ce que le précédent se termine.

Pour cette raison, concaténer unObservable, «chaud» qui commence à émettre des éléments immédiatement, conduira à la perte de tous les éléments que leObservable«chaud» émet avant que tous les éléments précédents ne soient terminés.

4.2. Count

L'opérateurcount émet le décompte de tous les éléments émis par la source:

Comptons le nombre d’éléments émis par unObservable:

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);

Si la sourceObservable se termine par une erreur, lescount transmettront une erreur de notification sans émettre d'élément. Cependant, s’il ne se termine pas du tout, lecount n’émettra pas d’élément ni ne se terminera.

Pour l'opérationcount, il y a aussi l'opérateurcountLong, qui à la fin émet une valeurLong, pour les séquences qui peuvent dépasser la capacité d'unInteger.

4.3. Reduce

L'opérateurreduce réduit tous les éléments émis en un seul élément en appliquant la fonction d'accumulateur.

Ce processus se poursuit jusqu'à ce que tous les éléments soient émis, puis lesObservable, desreduce, émettent la valeur finale renvoyée par la fonction.

Voyons maintenant comment il est possible de réduire une liste deString, en les concaténant dans l’ordre inverse:

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

L'opérateurcollect est similaire à l'opérateurreduce, mais il est dédié à la collecte d'éléments dans une seule structure de données mutable.

Il nécessite deux paramètres:

  • une fonction qui renvoie la structure de données mutable vide

  • une fonction qui, quand on lui donne la structure de données et un élément émis, modifie la structure de données de manière appropriée

Voyons comment il est possible de renvoyer unset d'éléments à partir d'unObservable:

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

L'opérateurtoList fonctionne exactement comme l'opérationcollect, mais rassemble tous les éléments dans une seule liste - pensez àCollectors.toList() de l'API Stream:

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

Comme dans l'exemple précédent mais la liste émise est triée:

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));

Comme nous pouvons le voir, letoSortedList utilise la comparaison par défaut, mais il est possible de fournir une fonction de comparateur personnalisée. Nous pouvons maintenant voir comment il est possible de trier les entiers dans un ordre inverse à l'aide d'une fonction de tri personnalisée:

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

L'opérateurtoMap convertit la séquence d'éléments émis par unObservable en une carte saisie par une fonction clé spécifiée.

En particulier, l'opérateurtoMap a différentes méthodes surchargées qui nécessitent un, deux ou trois des paramètres suivants:

  1. lekeySelector qui produit une clé à partir de l'élément

  2. levalueSelector qui produit à partir de l'élément émis la valeur réelle qui sera stockée dans la carte

  3. lemapFactory qui crée la collection qui contiendra les éléments

Commençons par définir une classe simpleBook:

class Book {
    private String title;
    private Integer year;

    // standard constructors, getters, and setters
}

Nous pouvons maintenant voir comment il est possible de convertir une série d'élémentsBook émis enMap, en ayant le titre du livre comme clé et l'année comme valeur:

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

Lors du mappage, il est très courant que plusieurs valeurs partagent la même clé. La structure de données qui mappe une clé à plusieurs valeurs s'appelle une carte multiple.

Ceci peut être réalisé avec l'opérateurtoMultiMap qui convertit la séquence d'éléments émis par unObservable en unList qui est également une carte saisie par une fonction clé spécifiée.

Cet opérateur ajoute un autre paramètre à ceux de l'opérateurtoMap, lescollectionFactory.. Ce paramètre permet de spécifier dans quel type de collection la valeur doit être stockée. Voyons comment cela peut être fait:

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. Conclusion

Dans cet article, nous avons exploré les opérateurs mathématiques et agrégés disponibles dans RxJava - et, bien sûr, un exemple simple d'utilisation de chacun.

Comme toujours, tous les exemples de code de cet article peuvent être trouvésover on Github.