RxJava操作符(03-變換操作)

版權聲明:本文爲openXu原創文章【openXu的博客】,未經博主允許不得以任何形式轉載

目錄:


  變換操作符的作用是對Observable發射的數據按照一定規則做一些變換操作,然後將變換後的數據發射出去,接下來看看RxJava中主要有哪些變換操作符:

1. Buffer

  定期收集Observable的數據放進一個數據包裹,然後發射這些數據包裹,而不是一次發射一個值。

  • Buffer操作符將一個Observable變換爲另一個,原來的Observable正常發射數據,變換產生的Observable發射這些數據的緩存集合。Buffer操作符在很多語言特定的實現中有很多種變體,它們在如何緩存這個問題上存在區別
  • 注意:如果原來的Observable發射了一個onError通知,Buffer會立即傳遞這個通知,而不是首先發射緩存的數據,即使在這之前緩存中包含了原始Observable發射的數據
  • Window操作符與Buffer類似,但是它在發射之前把收集到的數據放進單獨的Observable,而不是放進一個數據結構

在RxJava中有許多Buffer的變體(下面列舉兩個示例):

  • buffer(count): 以列表(List)的形式發射非重疊的緩存,每一個緩存至多包含來自原始Observable的count項數據(最後發射的列表數據可能少於count項)。

    這裏寫圖片描述

  • buffer(count, skip): 從原始Observable的第一項數據開始創建新的緩存,此後每當收到skip項數據,用count項數據填充緩存:開頭的一項和後續的count-1項,它以列表(List)的形式發射緩存,取決於count和skip的值,這些緩存可能會有重疊部分(比如skip < count時),也可能會有間隙(比如skip > count時)。
    通俗地講就是每隔skip個數據就往緩存中存入count個數據,比如原始數據爲(1,2,3,4,5,6,7,8,9),buffer(2,3)表示每隔3個數據往緩存集合中放2個數據,結果就是[1,2] [4,5] [7,8]

    這裏寫圖片描述

示例代碼:

//一組緩存3個數據
Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
        .buffer(3)
        .subscribe(i -> Log.d(TAG, "1buffer-count:" + i));
//每隔三個數據緩存2個數據
Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
        .buffer(2, 3)
        .subscribe(i -> Log.d(TAG, "1buffer-count&skip:" + i));

Observable.interval(1, TimeUnit.SECONDS).
        buffer(3, TimeUnit.SECONDS)
        .subscribe(i -> Log.d(TAG, "2buffer-count:" + i));
Observable.interval(1, TimeUnit.SECONDS).
        buffer(2, 3, TimeUnit.SECONDS)
        .subscribe(i -> Log.d(TAG, "2buffer-count&skip:" + i));

輸出:

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

1buffer-count&skip:[1, 2]
1buffer-count&skip:[4, 5]
1buffer-count&skip:[7, 8]
1buffer-count&skip:[10]

2buffer-count&skip:[0]
2buffer-count:[0, 1]
2buffer-count&skip:[3, 4]
2buffer-count:[2, 3, 4]
2buffer-count&skip:[5, 6]
2buffer-count:[5, 6, 7]
2buffer-count&skip:[8, 9]
2buffer-count:[8, 9, 10]
2buffer-count&skip:[11, 12]
2buffer-count:[11, 12, 13]

2 . FlatMap

vFlatMap將一個發射數據的Observable變換爲多個Observables,然後將它們發射的數據合併後放進一個單獨的Observable

    這裏寫圖片描述

  • FlatMap操作符使用一個指定的函數對原始Observable發射的每一項數據執行變換操作,這個函數返回一個本身也發射數據的Observable,然後FlatMap合併這些Observables發射的數據,最後將合併後的結果當做它自己的數據序列發射

  • 這個方法是很有用的,例如,當你有一個這樣的Observable:它發射一個數據序列,這些數據本身包含Observable成員或者可以變換爲Observable,因此你可以創建一個新的Observable發射這些次級Observable發射的數據的完整集合

  • 注意:FlatMap對這些Observables發射的數據做的是合併(merge)操作,因此它們可能是交錯的。

  • 在許多語言特定的實現中,還有一個操作符不會讓變換後的Observables發射的數據交錯,它按照嚴格的順序發射這些數據,這個操作符通常被叫作ConcatMap或者類似的名字

  • 注意:如果任何一個通過這個flatMap操作產生的單獨的Observable調用onError異常終止了,這個Observable自身會立即調用onError並終止。

  • 這個操作符有一個接受額外的int參數的一個變體。這個參數設置flatMap從原來的Observable映射Observables的最大同時訂閱數。當達到這個限制時,它會等待其中一個終止然後再訂閱另一個。

  • Javadoc: flatMap(Func1))

  • Javadoc: flatMap(Func1,int))

##flatMapIterable:
這個變體成對的打包數據,然後生成Iterable而不是原始數據和生成的Observables,但是處理方式是相同的

##concatMap
它類似於最簡單版本的flatMap,但是它按次序連接而不是合併那些生成的Observables,然後產生自己的數據序列。

##switchMap:
它和flatMap很像,除了一點:當原始Observable發射一個新的數據(Observable)時,它將取消訂閱並停止監視產生執之前那個數據的Observable,只監視當前這一個

示例代碼:

//將發射的數據都加上flat map的前綴
Observable.just(1, 2, 3, 4, 5)
        .flatMap(integer -> Observable.just("flat map:" + integer))
        .subscribe(i -> Log.d(TAG, i));
//會輸出n個n數字
Observable.just(1, 2, 3, 4)
        .flatMapIterable(
                integer -> {
                    ArrayList<Integer> s = new ArrayList<>();
                    for (int i = 0; i < integer; i++) {
                        s.add(integer);
                    }
                    return s;
                }
        )
        .subscribe(i -> Log.d(TAG, "flatMapIterable:" + i));

輸出:

flat map:1
flat map:2
flat map:3
flat map:4
flat map:5
flatMapIterable:1
flatMapIterable:2
flatMapIterable:2
flatMapIterable:3
flatMapIterable:3
flatMapIterable:3
flatMapIterable:4
flatMapIterable:4
flatMapIterable:4
flatMapIterable:4

3. GroupBy

    這裏寫圖片描述

  • GroupBy操作符將原始Observable分拆爲一些Observables集合,它們中的每一個發射原始Observable數據序列的一個子序列。哪個數據項由哪一個Observable發射是由函數getKey 判定的,這個函數給每一項指定一個Key,Key相同的數據會被同一個Observable發射。

  • groupBy操作符返回Observable的一個特殊子類GroupedObservable,實現了GroupedObservable接口的對象有一個額外的方法getKey,這個Key用於將數據分組到指定的Observable。

  • 注意:groupBy將原始Observable分解爲一個發射多個GroupedObservable的Observable,一旦有訂閱,每個GroupedObservable就開始緩存數據。因此,如果你忽略這些GroupedObservable中的任何一個,這個緩存可能形成一個潛在的內存泄露。因此,如果你不想觀察,也不要忽略GroupedObservable。你應該使用像take(0)這樣會丟棄自己的緩存的操作符。

  • 如果你取消訂閱一個GroupedObservable,那個Observable將會終止。如果之後原始的Observable又發射了一個與這個Observable的Key匹配的數據,groupBy將會爲這個Key創建一個新的GroupedObservable。

  • groupBy默認不在任何特定的調度器上執行。

    • Javadoc: groupBy(Func1)
    • Javadoc: groupBy(Func1,Func1)

示例代碼:

    private void op_GroupBy(TextView textView){

        // groupBy(Func1):Func1是對數據分組(確定key)
        Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9)
                .groupBy(new Func1<Integer, String>() {
                    @Override
                    public String call(Integer integer) {
                        //按照奇數和偶數分組
                        return integer % 2 == 0 ? "偶數" : "奇數";
                    }
                }).subscribe(new Action1<GroupedObservable<String, Integer>>() {
            @Override
            public void call(GroupedObservable<String, Integer> groupedObservable) {
//                groupedObservable.count()
//                        .subscribe(integer -> Log.v(TAG, "key" + groupedObservable.getKey() + " contains:" + integer + " numbers"));
                groupedObservable.subscribe(value->Log.v(TAG, "key" + groupedObservable.getKey() + " value:"+value));
            }
        });

        // groupBy(Func1,Func1):Func1是對數據分組(確定key),Func2發射每個數據,在這裏面可以對原始數據做處理
        Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9)
                .groupBy(new Func1<Integer, String>() {
                    @Override
                    public String call(Integer integer) {
                        //按照奇數和偶數分組
                        return integer % 2 == 0 ? "偶數" : "奇數";
                    }
                }, new Func1<Integer, String>() {
                    @Override
                    public String call(Integer integer) {
                        //在數字前面加上說明,如果不加這個參數,最後發射的數據就是原始整數
                        return (integer % 2 == 0 ? "偶數" : "奇數")+integer;
                    }
                }).subscribe(new Action1<GroupedObservable<String, String>>() {
            @Override
            public void call(GroupedObservable<String, String> groupedObservable) {
//                groupedObservable.count()
//                        .subscribe(integer -> Log.v(TAG, "key" + groupedObservable.getKey() + " contains:" + integer + " numbers"));
                groupedObservable.subscribe(value->Log.v(TAG, "key" + groupedObservable.getKey() + " value:"+value));
            }
        });
    }

輸出:

key奇數 value:1
key偶數 value:2
key奇數 value:3
key偶數 value:4
key奇數 value:5
key偶數 value:6
key奇數 value:7
key偶數 value:8
key奇數 value:9
key奇數 value:奇數1
key偶數 value:偶數2
key奇數 value:奇數3
key偶數 value:偶數4
key奇數 value:奇數5
key偶數 value:偶數6
key奇數 value:奇數7
key偶數 value:偶數8
key奇數 value:奇數9

4. Map/ cast

  • Map操作符對原始Observable發射的每一項數據應用一個你選擇的函數,然後返回一個發射這些結果的Observable。默認不在任何特定的調度器上執行。
    Map操作符就像一個工人,而Observable就是工廠流水線,map會將從流水線上遊傳下來的產品裝上螺釘,然後傳給後面的工人,每一個產品經過map都會被包裝一層(安裝螺釘)

    這裏寫圖片描述

  • cast操作符將原始Observable發射的每一項數據都強制轉換爲一個指定的類型(多態),然後再發射數據,它是map的一個特殊版本

示例代碼:

String[] names = {"張三", "李四", "王二", "麻子"};
//map
Observable.from(names).map(new Func1<String, String>() {
    @Override
    public String call(String s) {
        //將原始Observable發射的每一項數據前面加上 “姓名:”
        return "姓名:"+s;
    }
}).subscribe(new Action1<String>() {
    @Override
    public void call(String s) {
        Log.v(TAG, s);
    }
});

//cast: 多態中可以將父類引用強轉爲子類對象
//cast的強轉只適用於多態,而不適用於String強轉爲Integer
Animal animal = new Dog();  //多態
Observable.just(animal)
        .cast(Dog.class)
        .subscribe(new Action1<Dog>() {
           @Override
           public void call(Dog dog) {
               Log.v(TAG, "Cast ->" + dog);
           }
       });

輸出:

姓名:張三
姓名:李四
姓名:王二
姓名:麻子

Cast ->com.openxu.rxjava.operators.TransformOperators$Dog@52729440

5. Scan

  • Scan操作符對原始Observable發射的第一項數據應用一個函數,然後將那個函數的結果作爲自己的第一項數據發射。它將函數的結果同第二項數據一起填充給這個函數來產生它自己的第二項數據。它持續進行這個過程來產生剩餘的數據序列。這個操作符在某些情況下被叫做accumulator

    這裏寫圖片描述

    這裏寫圖片描述

  • 有一個scan操作符的變體,你可以傳遞一個種子值給累加器函數的第一次調用(Observable發射的第一項數據)。如果你使用這個版本,scan將發射種子值作爲自己的第一項數據。注意:傳遞null作爲種子值與不傳遞是不同的,null種子值是合法的。
  • 這個操作符默認不在任何特定的調度器上執行

示例代碼:

//會把原始數據的第一項當做新的第一項發射
Observable.just(1, 2, 3, 4, 5)
    .scan(new Func2<Integer, Integer, Integer>() {
        @Override
        public Integer call(Integer sum, Integer item) {
            Log.v(TAG, ">應用函數:" + sum+" ,"+item);
            return sum + item;
        }
    }).subscribe(new Action1<Integer>() {
        @Override
        public void call(Integer integer) {
            Log.v(TAG, "Next:" + integer);
        }
    });

//scan將發射種子值3作爲自己的第一項數據
Observable.just(1, 2, 3, 4, 5)
        .scan(2, new Func2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer sum, Integer item) {
                Log.d(TAG, ">應用函數:" + sum+" ,"+item);
                return sum + item;
            }
        }).subscribe(new Action1<Integer>() {
    @Override
    public void call(Integer integer) {
        Log.d(TAG, "Next:" + integer);
    }
});

輸出:

Next:1
應用函數:1 ,2
Next:3
應用函數:3 ,3
Next:6
應用函數:6 ,4
Next:10
應用函數:10 ,5
Next:15

Next:2
應用函數:2 ,1
Next:3
應用函數:3 ,2
Next:5
應用函數:5 ,3
Next:8
應用函數:8 ,4
Next:12
應用函數:12 ,5
Next:17

6. Window

    這裏寫圖片描述

  • Window和Buffer類似,但不是發射來自原始Observable的數據包,它發射的是Observables,這些Observables中的每一個都發射原始Observable數據的一個子集,最後發射一個onCompleted通知

  • 和Buffer一樣,Window有很多變體,每一種都以自己的方式將原始Observable分解爲多個作爲結果的Observable,每一個都包含一個映射原始數據的window。用Window操作符的術語描述就是,當一個窗口打開(when a window “opens”)意味着一個新的Observable已經發射(產生)了,而且這個Observable開始發射來自原始Observable的數據;當一個窗口關閉(when a window “closes”)意味着發射(產生)的Observable停止發射原始Observable的數據,並且發射終止通知onCompleted給它的觀察者們

示例代碼:

Observable.just(1, 2, 3, 4, 5, 6, 7)
        .window(3) //每次發射出一個包含三個整數的子Observable
        .subscribe(new Action1<Observable<Integer>>() {
                       @Override
                       public void call(Observable<Integer> integerObservable) {
                           //每次發射一個子Observable
                           Log.d(TAG,integerObservable+"");
                           //訂閱子Observable
                           integerObservable.subscribe(new Action1<Integer>() {
                               @Override
                               public void call(Integer integer) {
                                   Log.d(TAG,"window:" + integer);
                               }
                           });
                       }
                 });

Observable.just(1, 2, 3, 4, 5, 6, 7)
        .window(3, 2) //每次發射出一個包含三個整數的子Observable
        .subscribe(new Action1<Observable<Integer>>() {
            @Override
            public void call(Observable<Integer> integerObservable) {
                Log.d(TAG,integerObservable+"");
                //訂閱子Observable
                integerObservable.subscribe(new Action1<Integer>() {
                    @Override
                    public void call(Integer integer) {
                        Log.d(TAG,"windowSkip:" + integer);
                    }
                });
            }
        });

輸出:

rx.internal.operators.UnicastSubject@52715e4c
window:1
window:2
window:3
rx.internal.operators.UnicastSubject@527164fc
window:4
window:5
window:6
rx.internal.operators.UnicastSubject@527167f0
window:7

rx.internal.operators.UnicastSubject@52717474
windowSkip:1
windowSkip:2
rx.internal.operators.UnicastSubject@52717980
windowSkip:3
windowSkip:3
windowSkip:4
rx.internal.operators.UnicastSubject@52717cac
windowSkip:5
windowSkip:5
windowSkip:6
rx.internal.operators.UnicastSubject@52717fd8
windowSkip:7
windowSkip:7

  以上就是RxJava實現的變換相關的操作符,對於不能理解的童鞋,建議將源碼運行後對照分析,這樣有助於理解。

有問題請留言,有幫助請點贊(__)

#源碼下載:
https://github.com/openXu/RxJavaTest

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章