Operadores matemáticos e agregados no RxJava

Operadores matemáticos e agregados no RxJava

1. Introdução

Seguindo o artigointroduction to RxJava, veremos os operadores agregados e matemáticos.

These operations must wait for the source Observable to emit all items. Por causa disso, esses operadores são perigosos de usar emObservables que podem representar sequências muito longas ou infinitas.

Em segundo lugar, todos os exemplos usam uma instância deTestSubscriber, uma variedade particular deSubscriber que pode ser usada para testes de unidade, para realizar asserções, inspecionar eventos recebidos ou encerrar umSubscriber. simulado

Agora, vamos começar a olhar para os operadores matemáticos.

2. Configuração

Para usar operadores adicionais, precisaremosadd the additional dependency para opom.xml:


    io.reactivex
    rxjava-math
    1.0.0

Ou, para um projeto Gradle:

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

3. Operadores matemáticos

The MathObservable is dedicated to performing mathematical operations e seus operadores usam outroObservable que emite itens que podem ser avaliados como números.

3.1. Average

O operadoraverage emite um único valor - a média de todos os valores emitidos pela fonte.

Vamos ver isso em ação:

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

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

subscriber.assertValue(10);

Existem quatro operadores semelhantes para lidar com valores primitivos:averageInteger,averageLong,averageFloat eaverageDouble.

3.2. Max

O operadormax emite o maior número encontrado.

Vamos ver isso em ação:

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

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

subscriber.assertValue(9);

É importante notar que o operadormax tem um método sobrecarregado que usa uma função de comparação.

Considerando o fato de que os operadores matemáticos também podem trabalhar em objetos que podem ser gerenciados como números, o operador sobrecarregadomax permite a comparação de tipos personalizados ou classificação personalizada de tipos padrão.

Vamos definir a classeItem:

class Item {
    private Integer id;

    // standard constructors, getter, and setter
}

Agora podemos definir oitemObservablee usar o operadormax para emitir oItem com oid mais alto:

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

O operadormin __ emite um único item contendo o menor elemento da fonte:

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

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

subscriber.assertValue(1);

O operadormin tem um método sobrecarregado que aceita uma instância de comparador:

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

O operadorsum emite um único valor que representa a soma de todos os números emitidos pela fonteObservable:

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

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

subscriber.assertValue(210);

Existem também operadores semelhantes especializados primitivos:sumInteger,sumLong,sumFloat esumDouble.

4. Operadores agregados

4.1. Concat

O operadorconcat junta itens emitidos pela fonte em conjunto.

Vamos agora definir doisObservablese concatená-los:

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

Entrando em detalhes, o operadorconcat espera com a assinatura de cadaObservable adicional que é passado para ele até que o anterior seja concluído.

Por esta razão, concatenar umObservable, “quente” que começa a emitir itens imediatamente, levará à perda de quaisquer itens queObservable “quente” emite antes que todos os anteriores sejam concluídos.

4.2. Count

O operadorcount emite a contagem de todos os itens emitidos pela fonte:

Vamos contar o número de itens emitidos por umObservable:

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

Se a fonteObservable terminar com um erro, ocount passará um erro de notificação sem emitir um item. No entanto, se não terminar, ocount não emitirá um item nem será encerrado.

Para a operaçãocount, existe também o operadorcountLong, que no final emite um valorLong, para aquelas sequências que podem ultrapassar a capacidade de umInteger.

4.3. Reduce

O operadorreduce reduz todos os elementos emitidos em um único elemento aplicando a função de acumulador.

Esse processo continua até que todos os itens sejam emitidos e entãoObservable, dereduce, emita o valor final retornado da função.

Agora, vamos ver como é possível realizar a redução de uma lista deString, concatenando-os na ordem inversa:

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

O operadorcollect é semelhante ao operadorreduce, mas é dedicado a coletar elementos em uma única estrutura de dados mutável.

Requer dois parâmetros:

  • uma função que retorna a estrutura de dados mutáveis ​​vazia

  • uma função que, quando fornecida a estrutura de dados e um item emitido, modifica a estrutura de dados adequadamente

Vamos ver como pode ser possível retornarset de itens de umObservable:

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

O operadortoList funciona exatamente como a operaçãocollect, mas coleta todos os elementos em uma única lista - pense emCollectors.toList() da API de fluxo:

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

Assim como no exemplo anterior, mas a lista emitida é classificada:

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

Como podemos ver, otoSortedList usa a comparação padrão, mas é possível fornecer uma função comparadora personalizada. Agora podemos ver como é possível classificar os inteiros em ordem reversa usando uma função de classificação personalizada:

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

O operadortoMap converte a sequência de itens emitidos por umObservable em um mapa codificado por uma função de tecla especificada.

Em particular, o operadortoMap tem diferentes métodos sobrecarregados que requerem um, dois ou três dos seguintes parâmetros:

  1. okeySelector que produz uma chave do item

  2. ovalueSelector que produz a partir do item emitido o valor real que será armazenado no mapa

  3. omapFactory que cria a coleção que conterá os itens

Vamos começar definindo uma classe simplesBook:

class Book {
    private String title;
    private Integer year;

    // standard constructors, getters, and setters
}

Agora podemos ver como é possível converter uma série de itens emitidosBook emMap, tendo o título do livro como chave e o ano como o valor:

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

Ao mapear, é muito comum que muitos valores compartilhem a mesma chave. A estrutura de dados que mapeia uma chave para vários valores é chamada de multimap.

Isso pode ser alcançado com o operadortoMultiMap que converte a sequência de itens emitidos por umObservable em umList que também é um mapa codificado por uma função de tecla especificada.

Este operador adiciona outro parâmetro aos do operadortoMap, ocollectionFactory.. Este parâmetro permite especificar em qual tipo de coleção o valor deve ser armazenado. Vamos ver como isso pode ser feito:

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. Conclusão

Neste artigo, exploramos os operadores matemáticos e agregados disponíveis no RxJava - e, é claro, um exemplo simples de como usar cada um.

Como sempre, todos os exemplos de código neste artigo podem ser encontradosover on Github.