RxJavaでmap vs flatMapを使用するのはいつですか?


180

RxJavaでmap vs を使用するのflatMapいつですか?

たとえば、JSONを含むファイルをJSONを含む文字列にマップするとします。

を使用mapして、Exceptionなんとかして対処する必要があります。しかし、どうやって?:

Observable.from(jsonFile).map(new Func1<File, String>() {
    @Override public String call(File file) {
        try {
            return new Gson().toJson(new FileReader(file), Object.class);
        } catch (FileNotFoundException e) {
            // So Exception. What to do ?
        }
        return null; // Not good :(
    }
});

を使用するとflatMap、はるかに冗長にObservablesなりますが、別の場所を選択して再試行すれば、問題を連鎖的に転送してエラーを処理できます。

Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
    @Override public Observable<String> call(final File file) {
        return Observable.create(new Observable.OnSubscribe<String>() {
            @Override public void call(Subscriber<? super String> subscriber) {
                try {
                    String json = new Gson().toJson(new FileReader(file), Object.class);

                    subscriber.onNext(json);
                    subscriber.onCompleted();
                } catch (FileNotFoundException e) {
                    subscriber.onError(e);
                }
            }
        });
    }
});

私はの単純さが好きですmapが、flatmap(冗長ではなく)のエラー処理です。私はこれについてのベストプラクティスを見たことがなく、実際にこれがどのように使用されているのか知りたいです。

回答:


121

mapあるイベントを別のイベントに変換します。 flatMap1つのイベントを0個以上のイベントに変換します。(これはIntroToRxから取得されます

jsonをオブジェクトに変換したいので、mapを使用するだけで十分です。

FileNotFoundExceptionの処理は別の問題です(マップまたはフラットマップを使用してもこの問題は解決されません)。

例外の問題を解決するには、チェックされていない例外をスローします。RXはonErrorハンドラーを呼び出します。

Observable.from(jsonFile).map(new Func1<File, String>() {
    @Override public String call(File file) {
        try {
            return new Gson().toJson(new FileReader(file), Object.class);
        } catch (FileNotFoundException e) {
            // this exception is a part of rx-java
            throw OnErrorThrowable.addValueAsLastCause(e, file);
        }
    }
});

flatmapとまったく同じバージョン:

Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
    @Override public Observable<String> call(File file) {
        try {
            return Observable.just(new Gson().toJson(new FileReader(file), Object.class));
        } catch (FileNotFoundException e) {
            // this static method is a part of rx-java. It will return an exception which is associated to the value.
            throw OnErrorThrowable.addValueAsLastCause(e, file);
            // alternatively, you can return Obersable.empty(); instead of throwing exception
        }
    }
});

また、flatMapバージョンでは、単なるエラーである新しいObservableを返すこともできます。

Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
    @Override public Observable<String> call(File file) {
        try {
            return Observable.just(new Gson().toJson(new FileReader(file), Object.class));
        } catch (FileNotFoundException e) {
            return Observable.error(OnErrorThrowable.addValueAsLastCause(e, file));
        }
    }
});

2
これは、subscriber.onError()etcを呼び出しません。私が見たすべての例は、エラーをそのようにルーティングしました。それは問題ではありませんか?
クリストファーペリー

7
のコンストラクタはでOnErrorThrowableありprivateOnErrorThrowable.from(e)代わりに使用する必要があることに注意してください。
david.mihola 2015年

更新しました。OnErrorThrowable.from(e)は値を保持しないため、代わりにOnErrorThrowable.addValueAsLastCause(e、file)を使用します。これにより、値が保持されます。
dwursteisen 2015年

1
コード例は気に入っていますが、flatMap呼び出しのシグネチャを更新して、StringだけでなくObservable <String>を返すようにすると、技術的には2つの違いはありませんか?
Rich Ehmer、2015

78

FlatMapの動作はmapと非常によく似ています。違いは、適用する関数がオブザーバブル自体を返すため、非同期操作のマッピングに最適です。

実際的な意味では、Mapが適用する関数は、チェーンされた応答を変換するだけです(Observableを返さない)。FlatMapが適用される関数はを返しますObservable<T>が、メソッド内で非同期呼び出しを行う場合はFlatMapが推奨されます。

概要:

  • マップはタイプTのオブジェクトを返します
  • FlatMapはObservableを返します。

明確な例は、http//blog.couchbase.com/why-couchbase-chose-rxjava-new-java-sdkにあります。

Couchbase Java 2.XクライアントはRxを使用して、便利な方法で非同期呼び出しを提供します。Rxを使用しているため、メソッドmapとFlatMapがあるため、それらのドキュメントの説明は、一般的な概念を理解するのに役立ちます。

エラーを処理するには、susbcriberのonErrorをオーバーライドします。

Subscriber<String> mySubscriber = new Subscriber<String>() {
    @Override
    public void onNext(String s) { System.out.println(s); }

    @Override
    public void onCompleted() { }

    @Override
    public void onError(Throwable e) { }
};

このドキュメントを確認すると役立つ場合があります:http : //blog.danlew.net/2014/09/15/grokking-rxjava-part-1/

RXでエラーを管理する方法についての良い情報源は、https//gist.github.com/daschl/db9fcc9d2b932115b679にあります。


要約は間違っています。MapとFlatMapは同じ型を返しますが、それらが適用する関数は異なる型を返します。
CoXier 2018年

61

入力と出力が1つしかないので、あなたのケースではマップが必要です。

map-提供された関数は単にアイテムを受け入れ、さらに(1回だけ)放出されるアイテムを返します。

flatMap-提供された関数はアイテムを受け入れ、「Observable」を返します。つまり、新しい「Observable」の各アイテムは、さらに下に別々に放出されます。

コードがあなたのために物事を片付けるでしょう:

Observable.just("item1").map( str -> {
    System.out.println("inside the map " + str);
    return str;
}).subscribe(System.out::println);

Observable.just("item2").flatMap( str -> {
    System.out.println("inside the flatMap " + str);
    return Observable.just(str + "+", str + "++" , str + "+++");
}).subscribe(System.out::println);

出力:

inside the map item1
item1
inside the flatMap item2
item2+
item2++
item2+++

うまくいくかもしれませんが、マップの使用が最善のアイデアかどうかはわかりません。FileReaderが非同期呼び出しになると想定します。次に、マップをflatMapに変更する必要があります。マップとして残しておくと、予期したとおりにイベントが発生せず、混乱を招きます。RX Javaをまだ学習しているため、これに何度か噛まれました。flatMapは、物事が期待どおりに処理されることを確実にする確実な方法だと思います。
user924272

24

私が考える方法は、flatMap内部に配置したい関数がをmap()返すときに使用することですObservable。その場合は、まだ使用しようとするかもしれませんmap()が、実際的ではありません。その理由を説明させてください。

そのような場合に固執すると決めたならmap、あなたはを手に入れるでしょうObservable<Observable<Something>>。たとえば、あなたのケースで、架空のRxGsonライブラリを使用した場合、(単にを返すのではなく)メソッドObservable<String>からを返します。これは次のようになります。toJson()String

Observable.from(jsonFile).map(new Func1<File, Observable<String>>() {
    @Override public Observable<String>> call(File file) {
        return new RxGson().toJson(new FileReader(file), Object.class);
    }
}); // you get Observable<Observable<String>> here

この時点ではsubscribe()、そのような観測可能変数にかなり注意を払う必要があります。その内部では、値を取得Observable<String>するためsubscribe()に必要なを取得します。これは実用的ではなく、見栄えもよくありません。

したがって、有用なものにするための1つのアイデアは、このオブザーバブルをオブザーバブルで「フラット化」することです(_flat_Mapという名前の由来を確認し始めるかもしれません)。RxJavaは、オブザーバブルをフラット化するいくつかの方法を提供します。簡単にするために、マージが必要なものであると仮定ましょう。Mergeは基本的にオブザーバブルの束を取り、それらのいずれかが放出するときはいつでも放出します。(多くの人々は、スイッチがより良いデフォルトであると主張します。しかし、あなたが1つの値だけを放出しているならば、それはとにかく問題ではありません。)

前のスニペットを修正すると、次のようになります。

Observable.from(jsonFile).map(new Func1<File, Observable<String>>() {
    @Override public Observable<String>> call(File file) {
        return new RxGson().toJson(new FileReader(file), Object.class);
    }
}).merge(); // you get Observable<String> here

これ(またはマッピング、フィルタリングなど)にサブスクライブすると、String値を取得するだけなので、これははるかに便利です。(また、そのようなバリアントはmerge()RxJavaには存在しませんが、マージの概念を理解している場合は、それがどのように機能するかについても理解していただければ幸いです。)

したがって、基本的には、オブザーバブルを返すmerge()ことに成功した場合にのみ有用map()であり、繰り返し入力する必要がないためflatMap()、省略形として作成されました。通常map()と同じようにマッピング関数を適用しますが、後で戻り値を出力する代わりに、それらを「フラット化」(またはマージ)します。

これが一般的な使用例です。あらゆる場所でRxを使用するコードベースで最も役立ち、オブザーバブルを返す多くのメソッドがあり、オブザーバブルを返す他のメソッドとチェーンしたい場合。

ユースケースでは、でmap()放出された1つの値のみをで放出さonNext()れた別の値に変換できるため、これも便利ですonNext()。しかし、それを複数の値に変換することはできません。まったく値がないか、エラーになります。そしてakarnokdが彼の答えで書いたように(そして、おそらく彼は私よりもはるかに賢い、おそらく一般的にはですが、少なくともRxJavaに関しては)、から例外をスローするべきではありませんmap()。だからではなく、あなたが使用することができますflatMap()し、

return Observable.just(value);

すべてがうまくいくと、

return Observable.error(exception);

何かが失敗したとき。
完全なスニペットについては、彼の回答を参照してください:https : //stackoverflow.com/a/30330772/1402641


1
これは私の好意的な答えです。基本的に、メソッドが返すものであるオブザーバブルIFをオブザーバブルIFにネストすることになります。
filthy_wizard 2017年

21

問題は、RxJavaでmap vs flatMapをいつ使用するかです。。簡単なデモの方が具体的だと思います。

放出されたアイテムを別のタイプに変換する場合、ファイルを文字列に変換すると、mapとflatMapの両方が機能します。しかし、マップオペレーターの方がわかりやすいので、こちらを選びます。

しかし、どこかでflatMap魔法の仕事をすることがmapできますができません。たとえば、ユーザーの情報を取得したいのですが、ユーザーがログインしたときに最初にそのユーザーのIDを取得する必要があります。

さぁ、始めよう。

Observable<LoginResponse> login(String email, String password);

Observable<UserInfo> fetchUserInfo(String userId);

次に、2つのメソッドを示します。1つはログインで返されResponse、もう1つはユーザー情報を取得します。

login(email, password)
        .flatMap(response ->
                fetchUserInfo(response.id))
        .subscribe(userInfo -> {
            // get user info and you update ui now
        });

ご覧のとおり、関数flatMapが適用されます。最初にユーザーIDをResponse取得してから、ユーザー情報を取得します。2つのリクエストが完了すると、UIの更新やデータベースへのデータの保存などの作業を実行できます。

ただし、使用mapする場合、そのような素晴らしいコードを書くことはできません。つまり、flatMapリクエストのシリアル化に役立ちます。


18

ここでは簡単です親指のルール私は私が使用するときのように決定するのを助ける使用していることflatMap()以上のmap()受信者にはObservable

map変換を使用することを決定したら、オブジェクトを返す変換コードを記述しますか?

変換の最終結果として返されているものが次の場合:

  • 監視できないオブジェクトの場合は、単にを使用しmap()ます。そしてmap()、そのオブジェクトをObservableでラップして発行します。

  • Observableオブジェクトは、あなたが使用したいですflatMap()。そしてflatMap()、Observableのラップを解除し、返されたオブジェクトを選択し、それを独自のObservableでラップして放出します。

たとえば、入力パラメーターのTitled Cased Stringオブジェクトを返すメソッドtitleCase(String inputParam)があるとします。このメソッドの戻り値の型は、StringまたはObservable<String>です。

  • の戻り値の型titleCase(..)が単なるStringである場合は、使用しますmap(s -> titleCase(s))

  • 戻り値の型をした場合titleCase(..)であることをしたObservable<String>、あなたが使用したいですflatMap(s -> titleCase(s))

それが明確になることを願っています。


11

私はそれをに追加したかったのflatMapですが、関数内で独自のカスタムObservableを実際に使用する必要はなく、標準のファクトリメソッド/演算子に依存できます。

Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
    @Override public Observable<String> call(final File file) {
        try {
            String json = new Gson().toJson(new FileReader(file), Object.class);
            return Observable.just(json);
        } catch (FileNotFoundException ex) {
            return Observable.<String>error(ex);
        }
    }
});

一般に、RxJavaで可能な限り多くのセーフガードを配置したとしても、可能であれば、onXXXメソッドおよびコールバックから(Runtime-)例外をスローすることは避けてください。


でも地図で十分だと思います。それで、flatMapとmapは習慣でしょ?
CoXier 2018年

6

そのシナリオでのマップの使用では、新しいObservableは必要ありません。

これらのチェック済み例外をrxメカニズムに送信できるように、ラッパーであるExceptions.propagateを使用する必要があります

Observable<String> obs = Observable.from(jsonFile).map(new Func1<File, String>() { 
    @Override public String call(File file) {
        try { 
            return new Gson().toJson(new FileReader(file), Object.class);
        } catch (FileNotFoundException e) {
            throw Exceptions.propagate(t); /will propagate it as error
        } 
    } 
});

次に、サブスクライバーでこのエラーを処理する必要があります

obs.subscribe(new Subscriber<String>() {
    @Override 
    public void onNext(String s) { //valid result }

    @Override 
    public void onCompleted() { } 

    @Override 
    public void onError(Throwable e) { //e might be the FileNotFoundException you got }
};); 

それのための優れた投稿があります:http : //blog.danlew.net/2015/12/08/error-handling-in-rxjava/


0

場合によっては、オブザーバブルのチェーンができて、オブザーバブルが別のオブザーバブルを返すことがあります。'flatmap'は、最初のオブザーバブルに埋め込まれている2番目のオブザーバブルのラップを解除し、サブスクライブ中に2番目のオブザーバブルが吐き出しているデータに直接アクセスできるようにします。


0

フラットマップは、オブザーバブルをオブザーバブルにマッピングします。マップはアイテムをアイテムにマップします。

Flatmapはより柔軟ですが、Mapはより軽量で直接的なため、ユースケースに依存します。

非同期(スレッドの切り替えを含む)を実行している場合は、フラットマップを使用する必要があります。マップはコンシューマーが破棄されているかどうか(軽量性の一部)をチェックしないためです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.