[英]When do you use map vs flatMap in RxJava?
你什么時候在RxJava 中使用map
和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
的錯誤處理(不是冗長)。 我還沒有看到任何關於此的最佳實踐,我很好奇它在實踐中是如何使用的。
map
將一個事件轉換為另一個事件。 flatMap
將一個事件轉換為零個或多個事件。 (這是取自IntroToRx )
由於您想將 json 轉換為對象,因此使用 map 就足夠了。
處理 FileNotFoundException 是另一個問題(使用 map 或 flatmap 不能解決這個問題)。
要解決您的異常問題,只需拋出一個非檢查異常: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 的行為與 map 非常相似,不同之處在於它應用的函數本身返回一個 observable,因此它非常適合映射異步操作。
在實際意義上,函數 Map 應用只是對鏈式響應進行轉換(不返回 Observable); 雖然函數 FlatMap 應用返回一個Observable<T>
,這就是為什么如果您打算在方法內進行異步調用,建議使用 FlatMap 。
概括:
一個明顯的例子可以在這里看到: http : //blog.couchbase.com/why-couchbase-chose-rxjava-new-java-sdk 。
Couchbase Java 2.X Client 使用 Rx 以方便的方式提供異步調用。 由於它使用 Rx,它有方法 map 和 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/
可以在以下位置找到有關如何使用 RX 管理錯誤的良好來源: https : //gist.github.com/daschl/db9fcc9d2b932115b679
在您的情況下,您需要地圖,因為只有 1 個輸入和 1 個輸出。
map - 提供的函數只是接受一個項目並返回一個將進一步(僅一次)向下發射的項目。
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+++
問題是你什么時候在 RxJava 中使用 map 和 flatMap? . 我認為一個簡單的演示更具體。
當您想將發出的 item 轉換為另一種類型時,在您的情況下,將文件轉換為 String,map 和 flatMap 都可以工作。 但我更喜歡地圖運算符,因為它更清楚。
然而在某些地方, flatMap
可以做魔術而map
不能。 例如,我想獲取一個用戶的信息,但我必須在用戶登錄時首先獲取他的 id。顯然我需要兩個請求並且它們是有序的。
讓我們開始。
Observable<LoginResponse> login(String email, String password);
Observable<UserInfo> fetchUserInfo(String userId);
這里有兩種方法,一種用於登錄返回Response
,另一種用於獲取用戶信息。
login(email, password)
.flatMap(response ->
fetchUserInfo(response.id))
.subscribe(userInfo -> {
// get user info and you update ui now
});
如您所見,在 flatMap 函數中,首先我從Response
獲取用戶 ID,然后獲取用戶信息。 當兩個請求完成后,我們可以完成我們的工作,例如更新 UI 或將數據保存到數據庫中。
但是,如果您使用map
,則無法編寫如此出色的代碼。 總之, flatMap
可以幫助我們序列化請求。
我的想法是,當您想放入map()
的函數返回Observable
時,您可以使用flatMap
。 在這種情況下,您可能仍會嘗試使用map()
但這是不切實際的。 讓我試着解釋一下原因。
如果在這種情況下你決定堅持使用map
,你會得到一個Observable<Observable<Something>>
。 例如,在你的情況下,如果我們使用一個虛構的 RxGson 庫,它從它的toJson()
方法返回一個Observable<String>
(而不是簡單地返回一個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 會非常棘手。 在其中,您將獲得一個Observable<String>
,您將再次需要subscribe()
以獲取該值。 這不實用或不好看。
因此,為了使其有用,一個想法是“扁平化”這個可觀察的可觀察對象(您可能會開始看到名稱 _flat_Map 的來源)。 RxJava 提供了幾種扁平化 observable 的方法,為了簡單起見,我們假設合並是我們想要的。 Merge 基本上需要一堆 observables 並在其中任何一個發出時發出。 (很多人會認為switch是一個更好的默認值。但如果你只發出一個值,無論如何都無所謂。)
所以修改我們之前的代碼片段,我們會得到:
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()
可能只在成功map()
返回一個可觀察對象時才有用,所以你不必一遍又一遍地輸入, flatMap()
被創建為速記。 它像普通的map()
一樣應用映射函數,但稍后它不會發出返回的值,而是“扁平化”(或合並)它們。
這是一般用例。 它在到處使用 Rx 的代碼庫中最有用,並且您有許多返回 observable 的方法,您希望將這些方法與返回 observable 的其他方法鏈接起來。
在您的使用情況下,它碰巧是也很有用,因為map()
只能變換發出一個值onNext()
到發射的另一種價值onNext()
。 但它不能將其轉換為多個值,根本沒有值或錯誤。 正如akarnokd在他的回答中所寫(請注意,他比我聰明得多,可能在一般情況下,但至少在涉及 RxJava 時)您不應該從map()
拋出異常。 因此,您可以使用flatMap()
和
return Observable.just(value);
當一切順利時,但是
return Observable.error(exception);
當某事失敗時。
請參閱他對完整片段的回答: https : //stackoverflow.com/a/30330772/1402641
這是一個簡單的經驗法則,我使用它幫助我決定何時在 Rx 的Observable
使用flatMap()
不是map()
。
一旦您決定要使用map
轉換,您就會編寫轉換代碼來返回一些對象,對嗎?
如果您作為轉換的最終結果返回的是:
一個不可觀察的對象,那么您只需使用map()
。 map()
將該對象包裝在一個 Observable 中並發出它。
一個Observable
對象,然后你會使用flatMap()
。 flatMap()
解開 Observable,選擇返回的對象,用它自己的 Observable 包裝它並發出它。
例如,我們有一個方法 titleCase(String inputParam) 返回輸入參數的 Titled Cased String 對象。 此方法的返回類型可以是String
或Observable<String>
。
如果titleCase(..)
的返回類型僅僅是String
,那么您將使用map(s -> titleCase(s))
如果titleCase(..)
的返回類型是Observable<String>
,那么您將使用flatMap(s -> titleCase(s))
希望澄清。
我只是想用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);
}
}
});
通常,您應該盡可能避免從 onXXX 方法和回調中拋出(運行時)異常,即使我們在 RxJava 中放置了盡可能多的保護措施。
在那種情況下使用地圖,你不需要一個新的 Observable 。
您應該使用 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
}
}
});
然后你應該在訂閱者中處理這個錯誤
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/
在某些情況下,您最終可能會擁有一連串的 observable,其中您的 observable 會返回另一個 observable。 'flatmap' 可以解開隱藏在第一個中的第二個 observable,讓您直接訪問第二個 observable 在訂閱時吐出的數據。
Flatmap 將 observables 映射到 observables。 地圖將項目映射到項目。
Flatmap 更靈活,但 Map 更輕量和直接,因此這取決於您的用例。
如果您正在執行任何異步操作(包括切換線程),您應該使用 Flatmap,因為 Map 不會檢查消費者是否已處理(輕量級的一部分)
RxJava Map 與 FlatMap
它們都是轉換運算符,但map
具有 1-1 關系,而flatMap
具有 1-0 或多個關系。
map
和flatmap
發出的流只有 1 個元素用於map
或 0/many 用於flatmap
map
發出單個元素,而flatmap
發出元素流地圖運算符
map(new Function<A, B>() {
@Override
public User apply(A a) throws Exception {
B b = new B(a);
return b;
}
})
FlatMap 運算符
flatMap(new Function<A, ObservableSource<B>>() {
@Override
public ObservableSource<B> apply(A a) throws Exception {
return foo(a);
}
})
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.