RxJavaの数学演算子と集計演算子

1.はじめに

リンク:/rx-java[RxJavaの紹介]の記事に続いて、集約演算子と数学演算子について説明します。

  • これらの操作は、ソースの Observable がすべてのアイテムを発行するのを待たなければなりません** このため、これらの演算子は非常に長いまたは無限のシーケンスを表すことがある Observables に対して使用するのは危険です。

次に、すべての例で、単体テスト、アサーションの実行、受信したイベントの検査、または偽装された Subscriberのラップに使用できる TestSubscriber、 特定の種類の Subscriber__のインスタンスを使用します。

それでは、数学演算子について見てみましょう。

2.セットアップ

追加の演算子を使用するには、https://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22io.reactivex%22%20rxjava-math pom.xml:

<dependency>
    <groupId>io.reactivex</groupId>
    <artifactId>rxjava-math</artifactId>
    <version>1.0.0</version>
</dependency>

または、Gradleプロジェクトの場合:

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

3.数学演算子

  • MathObservable は数学演算の実行専用であり、その演算子は数値として評価できるアイテムを出力する別の Observable を使用します。

3.1. 平均

____演算子は、単一の値(ソースから発行されたすべての値の平均)を発行します。

実際に見てみましょう。

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

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

subscriber.assertValue(10);

プリミティブ値を処理するための4つの同様の演算子があります。

averageInteger averageLong averageFloat 、および averageDouble

3.2. マックス

m 演算子は最大の検出数を出力します。

実際に見てみましょう。

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

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

subscriber.assertValue(9);

max 演算子には、比較関数を取るオーバーロードされたメソッドがあることに注意することが重要です。

数学演算子は数値として管理できるオブジェクトにも対応できるという事実を考慮すると、 max オーバーロード演算子はカスタム型の比較や標準型のカスタムソートを可能にします。

Item クラスを定義しましょう。

class Item {
    private Integer id;

   //standard constructors, getter, and setter
}

これで itemObservable を定義してから、最大の id を持つ Item を発行するために max 演算子を使用できます。

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

TestSubscriber<Item> subscriber = TestSubscriber.create();

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

subscriber.assertValue(five);

3.3. 最小

min ____演算子は、ソースから最小の要素を含む単一の項目を生成します。

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

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

subscriber.assertValue(1);

min 演算子には、コンパレータインスタンスを受け取るオーバーロードメソッドがあります。

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

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

subscriber.assertValue(one);

3.4.

__演算子は、ソース Observableが発行したすべての数の合計を表す単一の値を発行します。

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

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

subscriber.assertValue(210);

sumInteger sumLong sumFloat 、および sumDouble のような、プリミティブに特化した類似の演算子もあります。

4.集約演算子

4.1. 連絡先

c 演算子は、ソースによって発行された項目を結合します _. _

それでは、2つの Observables を定義し、それらを連結しましょう。

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

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

TestSubscriber<Integer> subscriber = TestSubscriber.create();

Observable<Integer> concatObservable = observableOne
  .concatWith(observableTwo);

concatObservable.subscribe(subscriber);

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

詳細に説明すると、 concat 演算子は、前の引数が完了するまで、渡される追加の各 Observable をサブスクライブするのを待ちます。

このため、すぐにアイテムの発行を開始する「hot」 Observable を連結すると、直前のアイテムがすべて完了する前に、「hot」 Observable から発行されたアイテムがすべて失われます。

4.2. カウント

c 演算子は、ソースから発行されたすべての項目の数を発行します。

Observable から放出されたアイテムの数を数えましょう。

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

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

subscriber.assertValue(7);

ソースの Observable がエラーで終了した場合、 __はアイテムを発行せずに通知エラーを渡します。しかし、それがまったく終了しない場合、 c__はアイテムを発行も終了もしません。

count 操作には、 Integer の容量を超える可能性があるシーケンス用に、 congLong 演算子もあります。

4.3. 還元

r 演算子は、アキュムレータ関数を適用することによって、放出されたすべての要素を単一の要素に縮小します。

このプロセスはすべての項目が発行されるまで続き、その後 reduceからの Observable、__が関数から返された最終値を発行します。

それでは、 String のリストの縮小を逆の順序で連結して実行する方法を見てみましょう。

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

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

subscriber.assertValue("GFEDCBA");

4.4. 収集

collect 演算子は reduce 演算子に似ていますが、要素を単一の可変データ構造に集めることに特化しています。

2つのパラメータが必要です。

  • 空のミュータブルデータ構造を返す関数

  • データ構造と発行された項目が与えられたとき、

データ構造を適切に修正します

Observable からアイテムの set を返すことができる方法を見てみましょう:

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

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

subscriber.assertValues(new HashSet(list));

4.5. ToList

toList 演算子は collect 操作と同じように機能しますが、すべての要素を単一のリストにまとめます。StreamAPIの__Collectors.toList()について考えてください。

Observable<Integer> sourceObservable = Observable.range(1, 5);
TestSubscriber<List> subscriber = TestSubscriber.create();

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

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

4.6. ToSortedList

前の例と同じですが、発行されたリストはソートされています。

Observable<Integer> sourceObservable = Observable.range(10, 5);
TestSubscriber<List> subscriber = TestSubscriber.create();

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

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

ご覧のとおり、 toSortedList はデフォルトの比較を使用しますが、カスタムの比較関数を提供することも可能です。カスタムのソート機能を使用して、整数を逆の順序でソートすることがどのように可能であるかがわかります。

Observable<Integer> sourceObservable = Observable.range(10, 5);
TestSubscriber<List> subscriber = TestSubscriber.create();

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

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

4.7. ToMap

toMap 演算子は、 Observable によって発行された一連の項目を、指定されたキー機能によってキー設定されたマップに変換します。

特に、 toMap 演算子には、次のパラメータのうち1つ、2つ、または3つを必要とするさまざまなオーバーロードメソッドがあります。

  1. アイテムからキーを生成する keySelector

  2. 発行されたアイテムから実際のアイテムを生成する valueSelector

マップに格納される値 。アイテムを保持するコレクションを作成する mapFactory

単純なクラス Book を定義しましょう。

class Book {
    private String title;
    private Integer year;

   //standard constructors, getters, and setters
}

書籍のタイトルをキー、年を値の として、一連の発行された Book アイテムを Map__に変換する方法がわかります。

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

Observable<Map<String, Integer>> 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

マッピングするとき、多くの値が同じキーを共有することは非常に一般的です。 1つのキーを複数の値にマップするデータ構造はマルチマップと呼ばれます。

これは、 Observable によって発行された項目のシーケンスを、指定されたキー関数によってキー設定されたマップでもある List に変換する toMultiMap 演算子を使用して実現できます。

この演算子は、 toMap 演算子のパラメーター、__collectionFactoryに別のパラメーターを追加します。このパラメーターにより、値をどのコレクション型に格納するかを指定できます。これがどのように行われるのかを見てみましょう。

Observable<Book> bookObservable = Observable.just(
  new Book("The North Water", 2016),
  new Book("Origin", 2017),
  new Book("Sleeping Beauties", 2017)
);
TestSubscriber<Map> 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.まとめ

この記事では、RxJava内で使用可能な数学演算子と集約演算子について説明しました。もちろん、それぞれの使い方の簡単な例も紹介しました。

いつものように、この記事のすべてのコード例はhttps://github.com/eugenp/tutorials/tree/master/rxjava[over on Github]にあります。