前言
上一篇文章我們學(xué)習(xí)了創(chuàng)建類操作符,本篇我們將一起來學(xué)習(xí)RxJava轉(zhuǎn)換類操作符。所謂轉(zhuǎn)換,就是將事件序列中的對象或整個序列進行加工處理,轉(zhuǎn)換成不同的事件或事件序列。下面來看下轉(zhuǎn)換類操作符都有哪些及其使用場景。
初始化數(shù)據(jù)
還是使用系列第一篇的小區(qū)與房源的例子。先初始化假數(shù)據(jù)以便實踐操作符時使用。
//小區(qū)實體
public class Community {
private String communityName; //小區(qū)名稱
private List<House> houses; //房源集合
}
//房源實體
public class House {
private float size; //大小
private int floor; //樓層
private int price; //總價
private String decoration; //裝修程度
private String communityName; //小區(qū)名稱
}
private List<Community> communities;
private void initData() {
communities = new ArrayList<>();
List<House> houses1 = new ArrayList<>();
for (int i = 0; i < 5; i++) {
if (i % 2 == 0) {
houses1.add(new House(105.6f, i, 200, "簡單裝修", "東方花園"));
} else {
houses1.add(new House(144.8f, i, 520, "豪華裝修", "東方花園"));
}
}
communities.add(new Community("東方花園", houses1));
List<House> houses2 = new ArrayList<>();
for (int i = 0; i < 5; i++) {
if (i % 2 == 0) {
houses2.add(new House(88.6f, i, 166, "中等裝修", "馬德里春天"));
} else {
houses2.add(new House(123.4f, i, 321, "精致裝修", "馬德里春天"));
}
}
communities.add(new Community("馬德里春天", houses2));
List<House> houses3 = new ArrayList<>();
for (int i = 0; i < 5; i++) {
if (i % 2 == 0) {
houses3.add(new House(188.7f, i, 724, "豪華裝修", "帝豪家園"));
} else {
houses3.add(new House(56.4f, i, 101, "普通裝修", "帝豪家園"));
}
}
communities.add(new Community("帝豪家園", houses3));
}
轉(zhuǎn)換操作符
Map
map操作符,接收一個指定的Func1類型對象,然后將其應(yīng)用到每一個由Observable發(fā)射的值上,進而將發(fā)射的值轉(zhuǎn)換為我們期望的值。來看一下原理圖與實例:

//將一組Integer轉(zhuǎn)換成String
Observable.just(1, 2, 3, 4, 5)
.map(new Func1<Integer, String>() {
@Override
public String call(Integer integer) {
return "This is " + integer;
}
})
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
Log.e("rx_test", s);
}
});
//將Community集合轉(zhuǎn)換為每一個Community并獲取其name
Observable.from(communities)
.map(new Func1<Community, String>() {
@Override
public String call(Community community) {
return community.getCommunityName();
}
})
.subscribe(new Action1<String>() {
@Override
public void call(String communityName) {
Log.e("rx_test", "小區(qū)名稱為:" + communityName);
}
});
輸出結(jié)果:
This is 1
This is 2
This is 3
This is 4
This is 5
小區(qū)名稱為:東方花園
小區(qū)名稱為:馬德里春天
小區(qū)名稱為:帝豪家園
由輸出結(jié)果可看出,map操作符可用來進行數(shù)據(jù)的類型轉(zhuǎn)換,拼接或者對集合進行遍歷等1對1的轉(zhuǎn)換。第一個例子中,Func1<Integer, String>()第一個參數(shù)是發(fā)射數(shù)據(jù)當(dāng)前的類型,第二個參數(shù)是轉(zhuǎn)換之后的數(shù)據(jù)類型。Action1<String>中參數(shù)也為發(fā)射數(shù)據(jù)轉(zhuǎn)換之后的數(shù)據(jù)類型。
注意數(shù)據(jù)類型需對應(yīng)準(zhǔn)確,不要弄錯了。
FlatMap
flatMap操作符,也是用來轉(zhuǎn)換的,但與map操作符不同之處是,flatMap()返回的是Observable對象,且這個Observable對象并不是被直接發(fā)送到了 Subscriber的回調(diào)方法中。
這么說可能不易理解,我們來看小區(qū)與房的例子,現(xiàn)在有3個小區(qū),如果我們想打印出這3個小區(qū)中所有房源的信息,通過RxJava要如何做到?按照之前學(xué)習(xí)的我們或許會這么實現(xiàn):
Observable.from(communities)
.subscribe(new Action1<Community>() {
@Override
public void call(Community community) {
for (House house : community.getHouses()) {
Log.e("rx_test", "flatMap:小區(qū)名稱:" + house.getCommunityName()
+ ",價格:" + house.getPrice() + ",樓層:" + house.getFloor());
}
}
});
按照這種實現(xiàn)方法我們只可獲取到每個小區(qū)這一層,想要獲取小區(qū)中的房源還需進行一層for循環(huán)遍歷,這就違背了RxJava的原則了。那么來看下flatMap()如何實現(xiàn):
Observable.from(communities)
.flatMap(new Func1<Community, Observable<House>>() {
@Override
public Observable<House> call(Community community) {
return Observable.from(community.getHouses());
}
})
.subscribe(new Action1<House>() {
@Override
public void call(House house) {
Log.e("rx_test", "flatMap:小區(qū)名稱:" + house.getCommunityName()
+ ",價格:" + house.getPrice() + ",樓層:" + house.getFloor());
}
});
這樣的代碼是不是看起來舒心多了,再來看下flatMap()是如何實現(xiàn)的。
首先from()接收到小區(qū)集合communities后為其創(chuàng)建了一個Observable,依次將每個小區(qū)傳遞給flatMap(),flatMap()在每次接收到小區(qū)后會將其中包含的房源集合拿出來又創(chuàng)建了一個房源Observable,并激活這個房源Observable讓其開始發(fā)射事件,之后返回給小區(qū)集合的Observable,最后小區(qū)集合的Observable再將這些事件統(tǒng)一交給Subscriber的回調(diào)方法去處理。
整個過程有兩級Observable在運作,相當(dāng)于將小區(qū)集合Observable這個初始對象鋪平之后再通過統(tǒng)一路徑分發(fā)下去,鋪平這個工作就是flatMap所做的。
輸出結(jié)果:
flatMap:小區(qū)名稱:東方花園,價格:200,樓層:0
flatMap:小區(qū)名稱:東方花園,價格:520,樓層:1
flatMap:小區(qū)名稱:東方花園,價格:200,樓層:2
flatMap:小區(qū)名稱:東方花園,價格:520,樓層:3
flatMap:小區(qū)名稱:東方花園,價格:200,樓層:4
flatMap:小區(qū)名稱:馬德里春天,價格:166,樓層:0
flatMap:小區(qū)名稱:馬德里春天,價格:321,樓層:1
flatMap:小區(qū)名稱:馬德里春天,價格:166,樓層:2
flatMap:小區(qū)名稱:馬德里春天,價格:321,樓層:3
flatMap:小區(qū)名稱:馬德里春天,價格:166,樓層:4
flatMap:小區(qū)名稱:帝豪家園,價格:724,樓層:0
flatMap:小區(qū)名稱:帝豪家園,價格:101,樓層:1
flatMap:小區(qū)名稱:帝豪家園,價格:724,樓層:2
flatMap:小區(qū)名稱:帝豪家園,價格:101,樓層:3
flatMap:小區(qū)名稱:帝豪家園,價格:724,樓層:4
由輸出結(jié)果可看出這3個小區(qū)的所有房源信息都被依次打印了出來,但flatMap()有一個問題就是當(dāng)數(shù)據(jù)量過大時可能會出現(xiàn)輸出數(shù)據(jù)順序交錯的問題。
官方原理圖:

ConcatMap
concatMap操作符,與flatMap()功能類似。不同之處是concatMap()采用連接方式而不是合并方式,所以其發(fā)射的數(shù)據(jù)是嚴(yán)格按照順序的,這就解決了flatMap()有可能發(fā)生數(shù)據(jù)交錯的問題。
原理圖:

FlatMapIterable
flatMapIterable操作符,也與flatMap()相似,不同之處在于flatMapIterable轉(zhuǎn)化多個Observable是使用Iterable作為源數(shù)據(jù)的。
Observable.from(communities)
.flatMapIterable(new Func1<Community, Iterable<House>>() {
@Override
public Iterable<House> call(Community community) {
return community.getHouses();
}
}).subscribe(new Action1<House>() {
@Override
public void call(House house) {
Log.e("rx_test", "flatMap:小區(qū)名稱:" + house.getCommunityName()
+ ",價格:" + house.getPrice() + ",樓層:" + house.getFloor());
}
});
SwitchMap
switchMap轉(zhuǎn)換操作符,也與flatMap()相似,每當(dāng)源Observable發(fā)射新數(shù)據(jù)項(Observable)時,它將取消訂閱并停止監(jiān)視之前那個數(shù)據(jù)項產(chǎn)生Observable,并開始監(jiān)視當(dāng)前發(fā)射的這一個。
Observable.from(communities)
.switchMap(new Func1<Community, Observable<House>>() {
@Override
public Observable<House> call(Community community) {
return Observable.from(community.getHouses());
}
})
.subscribe(new Action1<House>() {
@Override
public void call(House house) {
Log.e("rx_test", "flatMap:小區(qū)名稱:" + house.getCommunityName()
+ ",價格:" + house.getPrice() + ",樓層:" + house.getFloor());
}
});
如之前的例子,當(dāng)數(shù)據(jù)量很大時,某一時刻,第一個小區(qū)所生成的小房源Observable正在發(fā)射數(shù)據(jù),這時第二個小區(qū)所生成的小房源Observable被激活,則第一個小區(qū)的小Observable就會被取消訂閱,其還未發(fā)射的數(shù)據(jù)也不在發(fā)射了。第二個小區(qū)小Observable開始發(fā)射數(shù)據(jù),之后都同理。
原理圖:

Scan
scan操作符,對一個序列的數(shù)據(jù)應(yīng)用一個函數(shù),并將這個函數(shù)的結(jié)果發(fā)射出去作為下個數(shù)據(jù)應(yīng)用函數(shù)時的第一個參數(shù)使用。
//例如:先輸出1,再將1+2=3作為下個數(shù)據(jù)發(fā)出,3+3=6再作為下個數(shù)據(jù)發(fā)出,以此類推。
Observable.just(1, 2, 3, 4, 5)
.scan(new Func2<Integer, Integer, Integer>() {
@Override
public Integer call(Integer integer, Integer integer2) {
return integer + integer2;
}
})
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
Log.e("rx_test", "scan:" + integer);
}
});
輸出結(jié)果:
scan:1
scan:3
scan:6
scan:10
scan:15
原理圖:

GroupBy
groupBy操作符,將原始Observable發(fā)射的數(shù)據(jù)按照key來拆分成一些小的Observable,然后這些小的Observable分別發(fā)射其所包含的的數(shù)據(jù)。通俗的說就是按照某個字段將數(shù)據(jù)進行分類再發(fā)射。
來看一個例子:有幾個小區(qū)的多套房源數(shù)據(jù),現(xiàn)在需要將其按照小區(qū)名稱進行分類并輸出。
List<House> houseList = new ArrayList<>();
houseList.add(new House(105.6f, 1, 200, "簡單裝修", "東方花園"));
houseList.add(new House(144.8f, 3, 300, "豪華裝修", "馬德里春天"));
houseList.add(new House(88.6f, 2, 170, "簡單裝修", "東方花園"));
houseList.add(new House(123.4f, 1, 250, "簡單裝修", "帝豪家園"));
houseList.add(new House(144.8f, 6, 350, "豪華裝修", "馬德里春天"));
houseList.add(new House(105.6f, 4, 210, "普通裝修", "東方花園"));
houseList.add(new House(188.7f, 3, 400, "精致裝修", "帝豪家園"));
houseList.add(new House(88.6f, 2, 180, "普通裝修", "東方花園"));
//根據(jù)小區(qū)名稱進行分類
Observable<GroupedObservable<String, House>> groupByCommunityNameObservable = Observable
.from(houseList)
.groupBy(new Func1<House, String>() {
@Override
public String call(House house) {
//提供分類規(guī)則的key
return house.getCommunityName();
}
});
Observable.concat(groupByCommunityNameObservable) //concat組合操作符,將多個Observable有序組合并發(fā)送,后期會詳細(xì)講解
.subscribe(new Action1<House>() {
@Override
public void call(House house) {
Log.e("rx_test", "groupBy:" + "小區(qū):" + house.getCommunityName() + ",價格:" + house.getPrice());
}
});
創(chuàng)建一個新的Observable:groupByCommunityNameObservable,它將會發(fā)送一個帶有GroupedObservable的序列(也就是指發(fā)送的數(shù)據(jù)項的類型為GroupedObservable)。GroupedObservable是一個特殊的Observable,它基于一個分組的key,在這個例子中的key就是小區(qū)名。
輸出結(jié)果:
groupBy:小區(qū):東方花園,價格:200
groupBy:小區(qū):東方花園,價格:170
groupBy:小區(qū):東方花園,價格:210
groupBy:小區(qū):東方花園,價格:180
groupBy:小區(qū):馬德里春天,價格:300
groupBy:小區(qū):馬德里春天,價格:350
groupBy:小區(qū):帝豪家園,價格:250
groupBy:小區(qū):帝豪家園,價格:400
原理圖:

總結(jié)
到此,本篇關(guān)于RxJava的常用轉(zhuǎn)換類操作符就講解完畢了,下一篇我們將一起研究RxJava的四類操作符中的過濾操作符都有哪些以及如何使用。
技術(shù)渣一枚,有寫的不對的地方歡迎大神們留言指正,有什么疑惑或者建議也可以在我Github上RxJavaDemo項目Issues中提出,我會及時回復(fù)。
附上RxJavaDemo的地址:
RxJavaDemo