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

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

1. 前書き

introduction to RxJavaの記事に続いて、集計演算子と数学演算子について見ていきます。

These operations must wait for the source Observable to emit all items.このため、これらの演算子は、非常に長いシーケンスまたは無限のシーケンスを表す可能性のあるObservablesで使用すると危険です。

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

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

2. セットアップ

追加の演算子を使用するには、add the additional dependencyからpom.xml:にする必要があります


    io.reactivex
    rxjava-math
    1.0.0

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

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

3. 数学演算子

The MathObservable is dedicated to performing mathematical operationsとその演算子は、数値として評価できる項目を発行する別のObservableを使用します。

3.1. Average

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

実際の動作を見てみましょう。

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

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

subscriber.assertValue(10);

プリミティブ値を処理するための4つの同様の演算子があります:averageIntegeraverageLongaverageFloat、およびaverageDouble

3.2. Max

max演算子は、検出された最大の数値を出力します。

実際の動作を見てみましょう。

Observable sourceObservable = Observable.range(1, 20);
TestSubscriber 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を定義してから、max演算子を使用して、idが最も高いItemを発行できます。

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

min __演算子は、ソースから最小の要素を含む単一のアイテムを出力します。

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

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

subscriber.assertValue(1);

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

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

sum演算子は、ソースObservable:によって発行されたすべての数値の合計を表す単一の値を発行します。

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

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

subscriber.assertValue(210);

プリミティブに特化した同様の演算子もあります:sumIntegersumLongsumFloat、およびsumDouble

4. 集計演算子

4.1. Concat

concat演算子は、ソースから発行されたアイテムを結合します.

次に、2つのObservablesを定義し、それらを連結してみましょう。

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

詳細に入ると、concatオペレーターは、前のオペレーターが完了するまで、渡される各追加のObservableをサブスクライブして待機します。

このため、アイテムの放出をすぐに開始する「ホット」Observable,を連結すると、以前のすべてのアイテムが完了する前に「ホット」Observableが放出するアイテムが失われます。

4.2. Count

count演算子は、ソースによって発行されたすべてのアイテムのカウントを発行します。

Observableによって放出されたアイテムの数を数えましょう:

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

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

count演算の場合、countLong演算子もあります。これは、Integerの容量を超える可能性のあるシーケンスに対して、最終的にLong値を出力します。

4.3. Reduce

reduce演算子は、アキュムレータ関数を適用することにより、放出されたすべての要素を1つの要素に減らします。

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

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

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

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

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

  • 空の可変データ構造を返す関数

  • データ構造と発行されたアイテムが与えられると、データ構造を適切に変更する関数

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

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

toList演算子はcollect操作と同じように機能しますが、すべての要素を1つのリストに収集します。StreamAPIからのCollectors.toList()について考えてみてください。

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

前の例と同じですが、出力されたリストはソートされます:

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

ご覧のとおり、toSortedListはデフォルトの比較を使用しますが、カスタムコンパレータ関数を提供することは可能です。 これで、カスタムの並べ替え関数を使用して、整数を逆の順序で並べ替えることができる方法を確認できます。

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

toMap演算子は、Observableによって発行されたアイテムのシーケンスを、指定されたキー関数によってキー設定されたマップに変換します。

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

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

  2. 放出されたアイテムからマップに格納される実際の値を生成するvalueSelector

  3. アイテムを保持するコレクションを作成するmapFactory

単純なクラスBookの定義を始めましょう:

class Book {
    private String title;
    private Integer year;

    // standard constructors, getters, and setters
}

これで、本のタイトルをキー、年を値:として、発行された一連のBookアイテムをMapに変換する方法を確認できます。

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

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

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

この演算子は、toMap演算子のパラメーターに別のパラメーターcollectionFactory.を追加します。このパラメーターを使用すると、値を格納するコレクションタイプを指定できます。 これを行う方法を見てみましょう。

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. 結論

この記事では、RxJava内で使用可能な数学演算子と集計演算子、およびもちろん、それぞれの使用方法の簡単な例を検討しました。

いつものように、この記事のすべてのコード例はover on Githubにあります。