你什么时候在RxJava中使用map vs flatMap?

你什么时候在RxJava中使用map vs flatMap?

举个例子,我们想把包含JSON的文件映射到包含JSON的string –

使用地图,我们必须以某种方式处理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,它更加冗长,但是我们可以将问题转发到Observable链,并且处理错误,如果我们select别的地方,甚至重试:

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

我喜欢简单的地图,但平面地图的error handling(而不是冗长)。 我还没有看到任何有关这方面的最佳做法,我很好奇这是如何在实践中使用的。

map将一个事件转换为另一个。 flatMap将一个事件转换为零个或多个事件。 (这是来自IntroToRx )

当你想把你的json转换成一个对象时,使用map就足够了。

处理FileNotFoundException是另一个问题(使用map或flatmap不能解决这个问题)。

为了解决你的exception问题,只要把它与一个非检查exception: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)); } } }); 

FlatMap的performance非常像map,不同之处在于它本身返回一个observable,所以它非常适合映射到asynchronous操作上。 Map不必发出与源Observable相同types的项目。

在实际意义上,Map只是对链式响应进行转换(不返回Observable); 而FlatMap返回一个Observable<T> ,这就是为什么如果你打算在方法内部进行一个asynchronous调用,build议使用FlatMap,那么:

  • 地图返回typesT的对象
  • FlatMap返回一个Observable<T>

一个清晰的例子可以在这里看到: http : //blog.couchbase.com/why-couchbase-chose-rxjava-new-java-sdk 。

Couchbase Java 2.X Client使用Rx以便利的方式提供asynchronous调用。 由于它使用Rx,它有方法映射和FlatMap,他们的文档中的解释可能有助于理解一般的概念。

要处理错误,请覆盖您的订阅者的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/

有关如何pipe理RX错误的好消息,请参阅: https : //gist.github.com/daschl/db9fcc9d2b932115b679

在你的情况下,我认为你需要地图,因为只有1个input和1个输出。

地图提供的function只是接受一个项目,并返回一个项目,将进一步发射(只有一次)。

flatMap – 提供的函数接受一个项目,然后返回一个“Observable”,这意味着新的“Observable”的每个项目将被进一步分开发射。

可能代码会清除你的东西。

  //START DIFFERENCE BETWEEN MAP AND FLATMAP 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); //END DIFFERENCE BETWEEN MAP AND FLATMAP 

输出:

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

我想到的方式是当你想放入map()的函数返回一个Observable时,你使用flatMap 。 在这种情况下,你仍然可以尝试使用map()但是这样做是不现实的。 让我试着解释为什么。

如果在这种情况下你决定坚持使用map ,你会得到一个Observable<Observable<Something>> 。 例如,在你的情况下,如果我们使用了一个虚构的RxGson库,从它的toJson()方法(而不是简单地返回一个String )返回一个Observable<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()来获取值。 这是不实际或不好看的。

所以为了使它有用一个想法是“扁平化”这个可观察的observable(你可能会开始看到_flat_Map的名字来自哪里)。 RxJava提供了一些方法来平坦观察者,为了简单起见,我们假设合并是我们想要的。 合并基本上需要大量的可观察物质,并在其中任何一个物体发射时发射。 (很多人会认为交换机会是一个更好的默认设置,但是如果只发出一个值,那么无论如何也没关系。)

修改我们以前的代码片段,我们会得到:

 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值。 (另外,请注意,RxJava中并不存在merge()这样的变体,但是如果你理解了合并的概念,那么我希望你也能理解这是如何工作的。)

所以基本上是因为这样的merge()应该只在成功返回一个observable的map()时才有用,所以你不需要一遍又一遍地input, flatMap()被创build为一个简写。 它像普通的map()一样应用映射函数,但是后来不是发射返回的值,而是“平滑”(或合并)它们。

这是一般用例。 在代码中使用Rx在代码中最有用,而且你有很多方法返回observable,你想用其他的方法返回observable。

在你的用例中它恰好也是有用的,因为map()只能将onNext()发射的一个值转换为onNext()发射的另一个值。 但它不能把它变成多重价值,根本没有价值或错误。 正如akarnokd在他的回答中所写的(并且介意他比我更聪明,一般来说,但至less在RxJava方面),你不应该从你的map()抛出exception。 所以相反,你可以使用flatMap()

 return Observable.just(value); 

一切顺利的时候,但是

 return Observable.error(exception); 

什么时候失败了
查看他的答案完整摘录: https : //stackoverflow.com/a/30330772/1402641

我只是想用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方法和callback中抛出(Runtime)exception。

下面是我使用的一个简单的缩略图规则 ,帮助我决定何时在Rx的Observable map()上使用flatMap()

一旦你决定要使用map转换,你会写你的转换代码来返回一些对象吗?

如果你正在返回的是你的转换的最终结果是:

  • 一个不可观察的对象,那么你只需要使用map()map()把这个对象包装在一个Observable中并发射出去。

  • 一个Observable对象,那么你可以使用flatMap() 。 而flatMap()展开Observable,拾取返回的对象,用它自己的Observable包装它并发射它。

举例来说,我们有一个方法titleCase(String inputParam),它返回input参数的带标题的string对象。 此方法的返回types可以是StringObservable<String>

  • 如果titleCase(..)的返回types仅仅是String ,那么你可以使用map(s -> titleCase(s))

  • 如果titleCase(..)的返回types是Observable<String> ,那么你可以使用flatMap(s -> titleCase(s))

希望澄清。

在这种情况下使用地图,你不需要一个新的Observable。

你应该使用Exceptions.propagate,这是一个包装,所以你可以发送这些检查的exception到rx机制

 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/

在某些情况下,你可能会得到一系列可观察的事物,其中你的可观察事物会返回另一个可观察事物。 'flatmap'类打开第一个被埋在第一个的第二个observable,让你直接访问数据第二个observable在订阅时吐出。