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つを必要とするさまざまなオーバーロードメソッドがあります。
-
アイテムからキーを生成する
keySelector
-
発行されたアイテムから実際のアイテムを生成する
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]にあります。