RxJava操作符之轉(zhuǎn)換操作符(四)

前言

上一篇文章我們學(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本篇文章介主要紹RxJava中操作符是以函數(shù)作為基本單位,與響應(yīng)式編程作為結(jié)合使用的,對什么是操作、操作符都有哪些...
    嘎啦果安卓獸閱讀 2,983評論 0 10
  • 注:只包含標(biāo)準(zhǔn)包中的操作符,用于個人學(xué)習(xí)及備忘參考博客:http://blog.csdn.net/maplejaw...
    小白要超神閱讀 2,376評論 2 8
  • 創(chuàng)建操作 用于創(chuàng)建Observable的操作符Create通過調(diào)用觀察者的方法從頭創(chuàng)建一個ObservableEm...
    rkua閱讀 1,957評論 0 1
  • 前言 按照官方的分類,操作符大致分為以下幾種: Creating Observables(Observable的創(chuàng)...
    小玉1991閱讀 1,116評論 0 1
  • RxJava正在Android開發(fā)者中變的越來越流行。唯一的問題就是上手不容易,尤其是大部分人之前都是使用命令式編...
    劉啟敏閱讀 2,014評論 1 7

友情鏈接更多精彩內(nèi)容