你什么时候在 RxJava 中使用 map 和 flatMap?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22847105/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When do you use map vs flatMap in RxJava?
提问by Christopher Perry
When do you use map
vs flatMap
in RxJava?
你什么时候在RxJava 中使用map
vs ?flatMap
Say, for example, we want to map Files containing JSON into Strings that contain the JSON--
举例来说,我们想将包含 JSON 的文件映射到包含 JSON 的字符串中——
Using map
, we have to deal with the Exception
somehow. But how?:
使用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 :(
}
});
Using flatMap
, it's much more verbose, but we can forward the problem down the chain of Observables
and handle the error if we choose somewhere else and even retry:
使用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);
}
}
});
}
});
I like the simplicity of the map
, but the error handling of flatmap
(not the verbosity). I haven't seen any best practices on this floating around and I'm curious how this is being used in practice.
我喜欢 的简单性map
,但喜欢 的错误处理flatmap
(不是冗长)。我还没有看到任何关于此的最佳实践,我很好奇它在实践中是如何使用的。
采纳答案by dwursteisen
map
transform one event to another.
flatMap
transform one event to zero or more event. (this is taken from IntroToRx)
map
将一个事件转化为另一个事件。
flatMap
将一个事件转换为零个或多个事件。(这是取自IntroToRx)
As you want to transform your json to an object, using map should be enough.
由于您想将 json 转换为对象,因此使用 map 就足够了。
Dealing with the FileNotFoundException is another problem (using map or flatmap wouldn't solve this issue).
处理 FileNotFoundException 是另一个问题(使用 map 或 flatmap 不能解决这个问题)。
To solve your Exception problem, just throw it with a Non checked exception : RX will call the onError handler for you.
要解决您的异常问题,只需抛出一个非检查异常: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);
}
}
});
the exact same version with flatmap :
与 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
}
}
});
You can return too, in the flatMap version a new Observable that is just an error.
你也可以返回,在 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));
}
}
});
回答by 1vand1ng0
FlatMap behaves very much like map, the difference is that the function it appliesreturns an observable itself, so it's perfectly suited to map over asynchronous operations.
FlatMap 的行为与 map 非常相似,不同之处在于它应用的函数本身返回一个 observable,因此它非常适合映射异步操作。
In the practical sense, the function Map applies just makes a transformation over the chained response (not returning an Observable); while the function FlatMap applies returns an Observable<T>
, that is why FlatMap is recommended if you plan to make an asynchronous call inside the method.
在实际意义上,函数 Map 应用只是对链式响应进行转换(不返回 Observable);虽然函数 FlatMap 应用返回一个Observable<T>
,这就是为什么如果您打算在方法内部进行异步调用,建议使用 FlatMap。
Summary:
概括:
- Map returns an object of type T
- FlatMap returns an Observable.
- Map 返回一个类型为 T 的对象
- FlatMap 返回一个 Observable。
A clear example can be seen here: http://blog.couchbase.com/why-couchbase-chose-rxjava-new-java-sdk.
一个明显的例子可以在这里看到:http: //blog.couchbase.com/why-couchbase-chose-rxjava-new-java-sdk。
Couchbase Java 2.X Client uses Rx to provide asynchronous calls in a convenient way. Since it uses Rx, it has the methods map and FlatMap, the explanation in their documentation might be helpful to understand the general concept.
Couchbase Java 2.X Client 使用 Rx 以方便的方式提供异步调用。由于它使用 Rx,它有方法 map 和 FlatMap,他们文档中的解释可能有助于理解一般概念。
To handle errors, override onError on your 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) { }
};
It might help to look at this document: http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/
查看此文档可能会有所帮助:http: //blog.danlew.net/2014/09/15/grokking-rxjava-part-1/
A good source about how to manage errors with RX can be found at: https://gist.github.com/daschl/db9fcc9d2b932115b679
可以在以下位置找到有关如何使用 RX 管理错误的良好来源:https: //gist.github.com/daschl/db9fcc9d2b932115b679
回答by mt.uulu
In your case you need map, since there is only 1 input and 1 output.
在您的情况下,您需要地图,因为只有 1 个输入和 1 个输出。
map - supplied function simply accepts an item and returns an item which will be emitted further (only once) down.
map - 提供的函数只是接受一个项目并返回一个将进一步(仅一次)向下发射的项目。
flatMap - supplied function accepts an item then returns an "Observable", meaning each item of the new "Observable" will be emitted separately further down.
flatMap - 提供的函数接受一个项目,然后返回一个“Observable”,这意味着新的“Observable”的每个项目将分别向下发射。
May be code will clear things up for you:
可能代码会为您解决问题:
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);
Output:
输出:
inside the map item1
item1
inside the flatMap item2
item2+
item2++
item2+++
回答by akarnokd
I just wanted to add that with flatMap
, you don't really need to use your own custom Observable inside the function and you can rely on standard factory methods/operators:
我只是想补充一点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);
}
}
});
Generally, you should avoid throwing (Runtime-) exceptions from onXXX methods and callbacks if possible, even though we placed as many safeguards as we could in RxJava.
通常,您应该尽可能避免从 onXXX 方法和回调中抛出(运行时)异常,即使我们在 RxJava 中放置了尽可能多的保护措施。
回答by ndori
In that scenario use map, you don't need a new Observable for it.
在这种情况下使用地图,你不需要一个新的 Observable 。
you should use Exceptions.propagate, which is a wrapper so you can send those checked exceptions to the rx mechanism
您应该使用 Exceptions.propagate,它是一个包装器,因此您可以将这些已检查的异常发送到 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
}
}
});
You then should handle this error in the subscriber
然后你应该在订阅者中处理这个错误
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 }
};);
There is an excellent post for it: http://blog.danlew.net/2015/12/08/error-handling-in-rxjava/
有一个很好的帖子:http: //blog.danlew.net/2015/12/08/error-handling-in-rxjava/
回答by Marcin Koziński
The way I think about it is that you use flatMap
when the function you wanted to put inside of map()
returns an Observable
. In which case you might still try to use map()
but it would be unpractical. Let me try to explain why.
我的想法是,flatMap
当您想放入其中的函数map()
返回一个Observable
. 在这种情况下,您可能仍会尝试使用,map()
但这将是不切实际的。让我试着解释一下原因。
If in such case you decided to stick with map
, you would get an Observable<Observable<Something>>
. For example in your case, if we used an imaginary RxGson library, that returned an Observable<String>
from it's toJson()
method (instead of simply returning a String
) it would look like this:
如果在这种情况下您决定坚持使用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
At this point it would be pretty tricky to subscribe()
to such an observable. Inside of it you would get an Observable<String>
to which you would again need to subscribe()
to get the value. Which is not practical or nice to look at.
在这一点上subscribe()
,这样一个可观察的对象是非常棘手的。在它里面你会得到一个Observable<String>
你再次需要subscribe()
得到值的。这不实用或不好看。
So to make it useful one idea is to "flatten" this observable of observables (you might start to see where the name _flat_Map comes from). RxJava provides a few ways to flatten observables and for sake of simplicity lets assume mergeis what we want. Merge basically takes a bunch of observables and emits whenever any of them emits. (Lots of people would argue switchwould be a better default. But if you're emitting just one value, it doesn't matter anyway.)
因此,为了使其有用,一个想法是“扁平化”这个可观察的可观察对象(您可能会开始看到名称 _flat_Map 的来源)。RxJava 提供了几种扁平化 observable 的方法,为了简单起见,我们假设合并是我们想要的。Merge 基本上需要一堆 observables 并在其中任何一个发出时发出。(很多人会认为switch是一个更好的默认值。但如果你只发出一个值,无论如何都无所谓。)
So amending our previous snippet we would get:
所以修改我们之前的代码片段,我们会得到:
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
This is a lot more useful, because subscribing to that (or mapping, or filtering, or...) you just get the String
value. (Also, mind you, such variant of merge()
does not exist in RxJava, but if you understand the idea of merge then I hope you also understand how that would work.)
这更有用,因为订阅那个(或映射,或过滤,或...)你只得到String
值。(另外,请注意,merge()
RxJava 中不存在这种变体,但是如果您了解合并的想法,那么我希望您也了解它是如何工作的。)
So basically because such merge()
should probably only ever be useful when it succeeds a map()
returning an observable and so you don't have to type this over and over again, flatMap()
was created as a shorthand. It applies the mapping function just as a normal map()
would, but later instead of emitting the returned values it also "flattens" (or merges) them.
所以基本上是因为merge()
只有当它成功map()
返回一个 observable时才可能有用,所以你不必一遍又一遍地输入它,它flatMap()
被创建为速记。它像平常一样应用映射函数map()
,但稍后它不会发出返回的值,而是“扁平化”(或合并)它们。
That's the general use case. It is most useful in a codebase that uses Rx allover the place and you've got many methods returning observables, which you want to chain with other methods returning observables.
这是一般用例。它在到处使用 Rx 的代码库中最有用,并且您有许多返回 observable 的方法,您希望将这些方法与返回 observable 的其他方法链接起来。
In your use case it happens to be useful as well, because map()
can only transform one value emitted in onNext()
into another value emitted in onNext()
. But it cannot transform it into multiple values, no value at all or an error. And as akarnokdwrote in his answer (and mind you he's much smarter than me, probably in general, but at least when it comes to RxJava) you shouldn't throw exceptions from your map()
. So instead you can use flatMap()
and
在您的用例中,它恰好也很有用,因为map()
只能将 中发出的一个值转换为 中发出的onNext()
另一个值onNext()
。但它不能将其转换为多个值,根本没有值或错误。正如akarnokd在他的回答中所写的那样(请注意,他比我聪明得多,可能在一般情况下,但至少在涉及 RxJava 时)你不应该从你的map()
. 因此,您可以使用flatMap()
和
return Observable.just(value);
when all goes well, but
当一切顺利时,但是
return Observable.error(exception);
when something fails.
See his answer for a complete snippet: https://stackoverflow.com/a/30330772/1402641
当某事失败时。
请参阅他对完整片段的回答:https: //stackoverflow.com/a/30330772/1402641
回答by karthiks
Here is a simple thumb-rulethat I use help me decide as when to use flatMap()
over map()
in Rx's Observable
.
下面是一个简单的拇指规则,我用帮我决定的时候使用flatMap()
过map()
的的Rx的Observable
。
Once you come to a decision that you're going to employ a map
transformation, you'd write your transformation code to return some Object right?
一旦决定要使用map
转换,您就会编写转换代码来返回一些对象,对吗?
If what you're returning as end result of your transformation is:
如果您作为转换的最终结果返回的是:
a non-observable object then you'd use just
map()
. Andmap()
wraps that object in an Observable and emits it.an
Observable
object, then you'd useflatMap()
. AndflatMap()
unwraps the Observable, picks the returned object, wraps it with its own Observable and emits it.
一个不可观察的对象,那么您只需使用
map()
. 并将map()
该对象包装在一个 Observable 中并发出它。一个
Observable
对象,然后你会使用flatMap()
. 并flatMap()
解开 Observable,选择返回的对象,用它自己的 Observable 包装它并发出它。
Say for example we've a method titleCase(String inputParam) that returns Titled Cased String object of the input param. The return type of this method can be String
or Observable<String>
.
例如,我们有一个方法 titleCase(String inputParam) 返回输入参数的 Titled Cased String 对象。此方法的返回类型可以是String
或Observable<String>
。
If the return type of
titleCase(..)
were to be mereString
, then you'd usemap(s -> titleCase(s))
If the return type of
titleCase(..)
were to beObservable<String>
, then you'd useflatMap(s -> titleCase(s))
如果返回类型
titleCase(..)
仅仅是String
,那么你会使用map(s -> titleCase(s))
如果返回类型
titleCase(..)
是Observable<String>
,那么你会使用flatMap(s -> titleCase(s))
Hope that clarifies.
希望澄清。
回答by Anoop Isaac
In some cases you might end up having chain of observables, wherein your observable would return another observable. 'flatmap' kind of unwraps the second observable which is buried in the first one and let you directly access the data second observable is spitting out while subscribing.
在某些情况下,您最终可能会拥有一连串的 observable,其中您的 observable 会返回另一个 observable。'flatmap' 可以解开隐藏在第一个中的第二个 observable,让您直接访问第二个 observable 在订阅时吐出的数据。
回答by CoXier
The question is When do you use map vs flatMap in RxJava?. And I think a simple demo is more specific.
问题是你什么时候在 RxJava 中使用 map 和 flatMap?. 我认为一个简单的演示更具体。
When you want to convert item emitted to another type , in your case converting file to String, map and flatMap can both work. But I prefer map operator because it's more clearly.
当您想将发出的 item 转换为另一种类型时,在您的情况下,将文件转换为 String,map 和 flatMap 都可以工作。但我更喜欢地图运算符,因为它更清楚。
However in some place, flatMap
can do magic work but map
can't. For example, I want to get a user's info but I have to first get his id when user login in. Obviously I need two requests and they are in order.
但是在某些地方,flatMap
可以做魔术但map
不能。例如,我想获取一个用户的信息,但我必须在用户登录时首先获取他的 id。显然我需要两个请求并且它们是有序的。
Let's begin.
让我们开始。
Observable<LoginResponse> login(String email, String password);
Observable<UserInfo> fetchUserInfo(String userId);
Here are two methods, one for login returned Response
, and another for fetching user info.
这里有两种方法,一种用于登录返回Response
,另一种用于获取用户信息。
login(email, password)
.flatMap(response ->
fetchUserInfo(response.id))
.subscribe(userInfo -> {
// get user info and you update ui now
});
As you see, in function flatMap applies, at first I get user id from Response
then fetch user info. When two requests are finished, we can do our job such as updating UI or save data into database.
如您所见,在函数 flatMap 应用中,首先我从中获取用户 ID,Response
然后获取用户信息。当两个请求完成后,我们可以完成我们的工作,例如更新 UI 或将数据保存到数据库中。
However if you use map
you can't write such nice code. In a word, flatMap
can help us serialize requests.
然而,如果你使用map
你就写不出这么好的代码。总之,flatMap
可以帮助我们序列化请求。
回答by skr1p7k1dd
Flatmap maps observables to observables. Map maps items to items.
Flatmap 将 observables 映射到 observables。地图将项目映射到项目。
Flatmap is more flexible but Map is more lightweight and direct, so it kind of depends on your usecase.
Flatmap 更灵活,但 Map 更轻量和直接,因此这取决于您的用例。
If you are doing ANYTHING async (including switching threads), you should be using Flatmap, as Map will not check if the consumer is disposed (part of the lightweight-ness)
如果您正在执行任何异步操作(包括切换线程),您应该使用 Flatmap,因为 Map 不会检查消费者是否已处理(轻量级的一部分)