RxJava操作符之Transforming Observables

ChuckChenLw發表於2016-07-09

  在RxJava操作符之Creating Observables 我們學會了怎麼建立Observable,但是我們專案中往往會遇到很多複雜的情況,需要我們對資料進行過濾和轉化,以得到我們想要的結果。這篇文章我們主要是學習怎麼轉化資料:
  Buffer
  FlatMap
  GroupBy
  Map
  Scan
  Window
  以上幾個操作符就是專門用來處理資料轉化功能的,接下來一個一個的來介紹吧。為了使程式碼看起來優雅一些,本篇開始都將使用lamda表示式。
  Buffer
  Buffer操作符就是將資料依據設定的大小做一下快取,然後將快取的資料作為一個集合發射出去。如下圖所示,第一張示例圖中我們指定buffer的大小為3,收集到3個資料後就發射出去,第二張圖中我們加入了一個skip引數用來指定每次發射一個集合需要跳過幾個資料,圖中如何指定count為2,skip為3,就會每3個資料發射一個包含兩個資料的集合,如果count==skip的話,我們就會發現其等效於第一種情況了。
  RxJava操作符之Transforming Observables
 
 RxJava操作符之Transforming Observables
 
  在程式碼中使用也是一目瞭然的,對第一種情況
  

 private Observable<Integer> getObservable() {
        return Observable.from(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
    }
    btn_buffer.setOnClickListener(v -> createObservable()
                .buffer(3)
                .subscribe(o -> Log.e("buffer", o + "")));

  會得到以下結果:

buffer: [1, 2,3]
buffer: [4, 5,6]
buffer: [7, 8,9]
buffer: [10]

  第二種情況只需要在buffer操作符多設定一個skip就好了:
  

 private Observable<Integer> getObservable() {
        return Observable.from(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
    }
    btn_buffer.setOnClickListener(v -> createObservable()
                .buffer(2, 3)
                .subscribe(o -> Log.e("buffer", o + "")));

  會得到以下結果:

buffer: [1, 2]
buffer: [4, 5]
buffer: [7, 8]
buffer: [10]

  Buffer操作符就是將資料依據設定的大小做一下快取,然後將快取的資料作為一個集合發射出去,Buffer不僅設定數量規則,還可以設定時間:
  

private Observable bufferTimeObserver() {
        return Observable.interval(1, TimeUnit.SECONDS).buffer(3, TimeUnit.SECONDS).observeOn(AndroidSchedulers.mainThread());
    }

  在上次我們講過Interval根據設定的時間發射一個資料,本例中是每秒發射一個資料,而buffer(3, TimeUnit.SECONDS),是設定每3秒將快取的資料發射出去。
  
  Map
  Map操作符會將資料直接 進行轉換,其和稍後要講的flatmap功能類似,但又有不同。先看map的原理圖:
   RxJava操作符之Transforming Observables
  從圖中可以看出,通過map操作符,設定資料轉換規則,將資料直接進行轉換。或許下圖能更清楚的數目map的作用:
   RxJava操作符之Transforming Observables
  其用法也是非常簡單的,下面我們就來驗證一下是否能得到想要的效果:

 private Observable<Integer> getObservable() {
        return Observable.from(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
    }
btn_map.setOnClickListener(v -> getObservable()
                .map(integer -> integer *10)
                .subscribe(integer1 -> Log.e("map", integer1 + "")));
map: 10
map: 20
map: 30
map: 40
map: 50
map: 60
map: 70
map: 80
map: 90
map: 100

  將所有發出的資料都乘以了10,顯然得到了我們想要的資料。下面接著介紹更加強大的flatmap
  
  Flatmap 
  FlatMap在專案中使用頻率很高的操作符,可以將要原資料(我們看做A型別的Observable)根據你想要的規則進行轉化成B型別Observable後再發射出去。其原理就是將這個Observable轉化為多個以原Observable發射的資料作為源資料的Observable,然後再將這多個Observable發射的資料整合發射出來,需要注意的是最後的順序可能會交錯地發射出來,如果需要順序輸出資料可以使用concatmap操作符。FlatMapIterable和FlatMap基相同,不同之處為其轉化的多個Observable是使用Iterable作為源資料的。
   RxJava操作符之Transforming Observables

  現在我們將原資料都加上flatmap字串怎麼實現呢?
  

 btn_flatmap.setOnClickListener(v -> getObservable()
                .flatMap(integer -> Observable.just("flatMap" + integer))
                .subscribe(s -> {
                    Log.e("flatmap", s);
                }));
                 private Observable<Integer> getObservable() {
        return Observable.from(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
    }

  注意和map操作符的區別,flatmap是需要將原始Observable轉換成目標Observable的,而map直接轉化資料
  以下是程式執行的結果:
  

flatmap: flatMap1
flatmap: flatMap2
flatmap: flatMap3
flatmap: flatMap4
flatmap: flatMap5
flatmap: flatMap6
flatmap: flatMap7
flatmap: flatMap8
flatmap: flatMap9
flatmap: flatMap10

  flatmap是需要好好掌握的,是一個功能強大的操作符。
  
  GroupBy
   GroupBy操作符將原始Observable發射的資料按照設定的key來拆分成一些小的Observable,然後這些小的Observable分別發射其所包含的的資料,類似資料庫查詢時groupBy。在使用中,我們需要提供一個生成key的規則,所有key相同的資料會分到同一個小的Observable中。
    RxJava操作符之Transforming Observables
 
   我們使用GroupBy把資料分成奇數和偶數,並且輸出偶數:

 btn_groupby.setOnClickListener(v -> getObservable()
                .groupBy(integer -> integer % 2 == 0)
                .subscribe(booleanIntegerGroupedObservable -> {
                    if (booleanIntegerGroupedObservable.getKey()) {
                        booleanIntegerGroupedObservable.subscribe(integer -> Log.e("groupby"
                                , integer + ""));
                    }

                }));
                private Observable<Integer> getObservable() {
        return Observable.from(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
    }

  執行結果如下,很明顯得到了想要的結果:

groupby: 2
groupby: 4
groupby: 6
groupby: 8
groupby: 10

  Scan
  Scan操作符對一個序列的資料如{1,2,3,4}應用一個函式a*b,並將這個函式的結果1+2=3發射出去作為下個資料3應用這個函式時候的第一個引數使用,也就是3+3=6,有點類似於遞迴操作。通過scan我們可以很容易的實現斐波拉契數列。
   RxJava操作符之Transforming Observables
 
   RxJava操作符之Transforming Observables

btn_scan.setOnClickListener(v -> getObservable()
                .scan((integer, integer2) -> integer +integer2)
                .subscribe(integer1 -> Log.e("scan", "" + integer1)));
                private Observable<Integer> getObservable() {
        return Observable.from(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
    }

  執行結果如下,

scan: 1
scan: 3
scan: 6
scan: 10
scan: 15
scan: 21
scan: 28
scan: 36
scan: 45
scan: 55

  Window
  Window操作符功能和我們前面講過的buffer類似,不同之處在於Window發射的是一些小的Observable物件,由這些小的Observable物件來發射內部包含的資料。同buffer一樣,window不僅可以通過數目來分組還可以通過時間等規則來分組
   RxJava操作符之Transforming Observables
  
  我們將{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}分成兩個Observable每個包含5個資料,看看window能否做到

btn_window.setOnClickListener(v -> getObservable()
                .window(5)
                .subscribe(integerObservable->{
                   integerObservable
                           .subscribe(integer -> Log.e("window"+integerObservable,integer+""));
                }));
                private Observable<Integer> getObservable() {
        return Observable.from(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
    }

  得到以下結果

windowrx.subjects.UnicastSubject@875d781: 1
windowrx.subjects.UnicastSubject@875d781: 2
windowrx.subjects.UnicastSubject@875d781: 3
windowrx.subjects.UnicastSubject@875d781: 4
windowrx.subjects.UnicastSubject@875d781: 5
windowrx.subjects.UnicastSubject@9c4026: 6
windowrx.subjects.UnicastSubject@9c4026: 7
windowrx.subjects.UnicastSubject@9c4026: 8
windowrx.subjects.UnicastSubject@9c4026: 9
windowrx.subjects.UnicastSubject@9c4026: 10

  可以清晰看到確實有兩個Observable,rx.subjects.UnicastSubject@875d781和rx.subjects.UnicastSubject@9c4026,每個Observable包含5個資料。也是驗證上邊的結論。
  
  總結:Transforming操作符,在Rxjava中經常使用,並且功能強大,所以一定要熟練的掌握轉換操作符。這樣才能靈活使用Rxjava。

相關文章