RxJava操作符之過濾操作符(五)

LeiHolmes發表於2017-10-31

前言

  上一篇文章我們學習了轉換類操作符,本篇我們將一起來學習RxJava過濾類操作符。過濾操作符主要是用來對事件資料進行過濾與篩選,只返回滿足條件的資料,一起來看下都有哪些。
  

過濾操作符

Filter

  filter操作符,按照一定的約束條件過濾序列中我們不想要的資料,只返回滿足條件的資料給觀察者。

//結合flatmap,過濾出各小區中房源大小大於120平的房子
Observable.from(communities)
        .flatMap(new Func1<Community, Observable<House>>() {
            @Override
            public Observable<House> call(Community community) {
                return Observable.from(community.getHouses());
            }
        })
        .filter(new Func1<House, Boolean>() {
            @Override
            public Boolean call(House house) {
                return house.getSize() > 120f;
            }
        })
        .subscribe(new Action1<House>() {
            @Override
            public void call(House house) {
                Log.e("rx_test", "filter:大於120平的房子:" + house.getCommunityName() + "小區,大小:" + house.getSize());
            }
        });複製程式碼

  由程式碼可見,我們需要new一個Func1物件給filter(),Func1<House, Boolean>()中第一個是由觀測序列傳入資料的型別,第二個是返回是否過濾的Boolean物件。滿足filter()的條件則返回true,否則返回false。並將返回為true的資料發射給觀察者。
  輸出結果:

filter:大於120平的房子:東方花園小區,大小:144.8
filter:大於120平的房子:東方花園小區,大小:144.8
filter:大於120平的房子:馬德里春天小區,大小:123.4
filter:大於120平的房子:馬德里春天小區,大小:123.4
filter:大於120平的房子:帝豪家園小區,大小:188.7
filter:大於120平的房子:帝豪家園小區,大小:188.7
filter:大於120平的房子:帝豪家園小區,大小:188.7複製程式碼

  原理圖:

  實際專案開發中,filter操作符可用來過濾資料集合中的null值,方便實用。

Take

  take(int count)操作符,可用來擷取觀測序列中前count個元素併發射。

//take:獲取前兩個小區名
Observable.from(communities)
        .take(2)
        .subscribe(new Action1<Community>() {
            @Override
            public void call(Community community) {
                Log.e("rx_test", "take:前兩個小區:" + community.getCommunityName());
            }
        });複製程式碼

  輸出結果:

take:前兩個小區:東方花園
take:前兩個小區:馬德里春天複製程式碼

  原理圖:

TakeLast

  takeLast(int count)操作符,顧名思義,擷取觀測序列中後count個元素併發射。

//takeLast:獲取後兩個小區名
Observable.from(communities)
        .takeLast(2)
        .subscribe(new Action1<Community>() {
            @Override
            public void call(Community community) {
                Log.e("rx_test", "takeLast:後兩個小區:" + community.getCommunityName());
            }
        });複製程式碼

  輸出結果:

takeLast:後兩個小區:馬德里春天
takeLast:後兩個小區:帝豪家園複製程式碼

  原理圖:

TakeUntil

  takeUntil操作符有兩種型別的入參。
  1.takeUntil(Observable)
  訂閱並開始發射原始Observable,同時監視我們提供的第二個Observable。如果第二個Observable發射了一項資料或者發射了一個終止通知,takeUntil()返回的Observable會停止發射原始Observable並終止。

//observableA每300ms發射一個Long型自增資料
//observableB每800ms發射一個Long型自增資料
Observable<Long> observableA = Observable.interval(300, TimeUnit.MILLISECONDS);
Observable<Long> observableB = Observable.interval(800, TimeUnit.MILLISECONDS);
observableA.takeUntil(observableB)
        .subscribe(new Subscriber<Long>() {
            @Override
            public void onCompleted() {
                Log.e("rx_test", "takeUntil(Observable):" + "onCompleted");
            }

            @Override
            public void onError(Throwable e) {
                Log.e("rx_test", "takeUntil(Observable):onError:" + e.getMessage());
            }

            @Override
            public void onNext(Long aLong) {
                Log.e("rx_test", "takeUntil(Observable):onNext:" + aLong);
            }
        });複製程式碼

  輸出結果:

takeUntil(Observable):onNext:0
takeUntil(Observable):onNext:1
takeUntil(Observable):onCompleted複製程式碼

  由輸出結果可看出,訂閱之後,observableA依次發射0,1之後就發射onCompleted標記停止了。這是由於observableA每300ms發射一次,當發射完1後,時間已過去600ms,到800ms時observableB開始發射資料,takeUntil起作用則中斷了observableA的發射。
  原理圖:

  2.takeUntil(Func1)
  通過傳入的Func1中的call()方法判斷是否中止發射資料。

//takeUntil:與flatmap結合過濾直到房價大於500時中斷當前小Observable發射House
Observable.from(communities)
        .flatMap(new Func1<Community, Observable<House>>() {
            @Override
            public Observable<House> call(Community community) {
                return Observable.from(community.getHouses());
            }
        })
        .takeUntil(new Func1<House, Boolean>() {
            @Override
            public Boolean call(House house) {
                return house.getPrice() > 500;
            }
        })
        .subscribe(new Action1<House>() {
            @Override
            public void call(House house) {
                Log.e("rx_test", "takeUntil:大於500時中斷髮射:" + house.getCommunityName() + "小區,房價:" + house.getPrice());
            }
        });複製程式碼

  輸出結果:

takeUntil(Func1):大於500時中斷髮射:東方花園小區,房價:200
takeUntil(Func1):大於500時中斷髮射:東方花園小區,房價:520複製程式碼

  原理圖:

TakeWhile

  takeWhile操作符,類似於takeUntil(Func1),不過takeWhile()是當Observable發射的資料不滿足條件時中止Observable的發射。

//takeWhile:當發射的資料等於3時中止發射
Observable.just(1, 2, 3, 4, 5)
        .takeWhile(new Func1<Integer, Boolean>() {
            @Override
            public Boolean call(Integer integer) {
                return integer != 3;
            }
        })
        .subscribe(new Action1<Integer>() {
            @Override
            public void call(Integer integer) {
                Log.e("rx_test", "takeWhile:" + integer);
            }
        });複製程式碼

  輸出結果:

takeWhile:1
takeWhile:2複製程式碼

  原理圖:

Skip

  skip(int count)操作符,忽略發射觀測序列的前count項資料。

//忽略前兩個小區資料
Observable.from(communities)
        .skip(2)
        .subscribe(new Action1<Community>() {
            @Override
            public void call(Community community) {
                Log.e("rx_test", "skip:忽略前兩個小區:" + community.getCommunityName());
            }
        });複製程式碼

  輸出結果:

skip:忽略前兩個小區:帝豪家園複製程式碼

  原理圖:

SkipLast

  skipLast(int count)操作符,忽略發射觀測序列的後count項資料。

//忽略後兩個小區資料
Observable.from(communities)
        .skipLast(2)
        .subscribe(new Action1<Community>() {
            @Override
            public void call(Community community) {
                Log.e("rx_test", "skip:忽略後兩個小區:" + community.getCommunityName());
            }
        });複製程式碼

  輸出結果:

忽略後兩個小區:東方花園複製程式碼

  原理圖:

SkipUntil

  skipUntil操作符,與takeUntil()相反。訂閱並開始發射原始Observable,同時監視我們提供的第二個Observable。如果第二個Observable發射了一項資料或者發射了一個終止通知,skipUntil()返回的Observable才會開始發射資料,忽略之前的資料項。
  原理圖:

SkipWhile

  skipWhile操作符,與takeWhile相反,當Observable發射的資料不滿足條件時才開始發射資料,忽略之前的資料項。
  原理圖:

Debounce

  debounce操作符有兩種型別的入參。
  1.debounce(long, TimeUnit)
  過濾由Observable發射的速率過快的資料,起到限流的作用。第一個引數為限流時間,第二個引數為時間單位。

Observable.create(new Observable.OnSubscribe<Integer>() {
    @Override
    public void call(Subscriber<? super Integer> subscriber) {
        try {
            for (int i = 1; i < 10; i++) {
                subscriber.onNext(i);
                Thread.sleep(i * 100); //分別延時100,200,300,400,500......900ms發射資料
            }
            subscriber.onCompleted();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).subscribeOn(Schedulers.newThread())
        .debounce(400, TimeUnit.MILLISECONDS)
        .subscribe(new Observer<Integer>() {
            @Override
            public void onCompleted() {
                Log.e("rx_test", "debounce:" + "onCompleted");
            }

            @Override
            public void onError(Throwable e) {
            }

            @Override
            public void onNext(Integer integer) {
                Log.e("rx_test", "debounce:" + integer);
            }
        });複製程式碼

  輸出結果:

debounce:5
debounce:6
debounce:7
debounce:8
debounce:9
debounce:onCompleted複製程式碼

  由輸出結果可以看出由於設定限流時間為500ms,所以1-4並沒有被髮射而是被過濾了。
  注意:如果源Observable產生的最後一個結果在限流時間內內呼叫了onCompleted,那麼通過debounce操作符也會把這個結果提交給訂閱者。
  原理圖:

  2.debounce(Func1)
  根據Func1的call方法中的函式來過濾。Func1中的中的call方法返回了一個臨時的Observable,如果原始的Observable在發射一個新的資料時,上一個資料根據Func1的call方法生成的臨時Observable還沒結束,那麼上一個資料就會被過濾掉。
  原理圖:

Distinct

  1.distinct()
  只允許還沒有發射過的資料通過,達到去除序列中重複項的作用。

//去除重複數字
Observable.just(1, 2, 2, 3, 4, 5, 6, 6, 6, 7)
        .distinct()
        .subscribe(new Action1<Integer>() {
            @Override
            public void call(Integer integer) {
                Log.e("rx_test", "distinct:去重:" + integer);
            }
        });複製程式碼

  輸出結果:

distinct:去重:1
distinct:去重:2
distinct:去重:3
distinct:去重:4
distinct:去重:5
distinct:去重:6
distinct:去重:7複製程式碼

  由輸出結果可見有重複的2和6都被過濾了。
  原理圖:

  2.distinct(Func1)
  根據Func1中的call方法進行去重,call方法會根據Observable發射的值生成一個Key,然後比較這個key來判斷兩個資料是否相同,如果判定為重複則會和distinct()一樣過濾掉重複的資料項。

//根據某屬性去重,去除各小區大小相同的房源
Observable.from(communities)
        .flatMap(new Func1<Community, Observable<House>>() {
            @Override
            public Observable<House> call(Community community) {
                return Observable.from(community.getHouses());
            }
        })
        .distinct(new Func1<House, Float>() {
            @Override
            public Float call(House house) {
                return house.getSize();
            }
        })
        .subscribe(new Action1<House>() {
            @Override
            public void call(House house) {
                Log.e("rx_test", "distinct(Func1):去重:" + house.getCommunityName() + "小區,大小:" + house.getSize());
            }
        });複製程式碼

  輸出結果:

distinct(Func1):去重:東方花園小區,大小:105.6
distinct(Func1):去重:東方花園小區,大小:144.8
distinct(Func1):去重:馬德里春天小區,大小:88.6
distinct(Func1):去重:馬德里春天小區,大小:123.4
distinct(Func1):去重:帝豪家園小區,大小:188.7
distinct(Func1):去重:帝豪家園小區,大小:56.4複製程式碼

DistinctUntilChanged

  1.distinctUntilChanged()
  通過當前資料項與前一項是否相同來進行去重。

//向前去重複資料
Observable.just(1, 2, 2, 3, 4, 2, 3, 5, 5)
        .distinctUntilChanged()
        .subscribe(new Action1<Integer>() {
            @Override
            public void call(Integer integer) {
                Log.e("rx_test", "distinctUntilChanged:向前去重:" + integer);
            }
        });複製程式碼

  輸出結果:

distinctUntilChanged:向前去重:1
distinctUntilChanged:向前去重:2
distinctUntilChanged:向前去重:3
distinctUntilChanged:向前去重:4
distinctUntilChanged:向前去重:2
distinctUntilChanged:向前去重:3
distinctUntilChanged:向前去重:5複製程式碼

  原理圖:

  2.distinctUntilChanged(Func1)
  與distinct(Func1)類似,根據Func1中call方法產生一個key來判斷相鄰兩個資料項是否相同。

//根據某屬性向前去重,去除各小區名相同的房源
Observable.from(communities)
        .flatMap(new Func1<Community, Observable<House>>() {
            @Override
            public Observable<House> call(Community community) {
                return Observable.from(community.getHouses())
                .distinctUntilChanged(new Func1<House, String>() {
                    @Override
                    public String call(House house) {
                        return house.getCommunityName();
                    }
                });
            }
        })
        .subscribe(new Action1<House>() {
            @Override
            public void call(House house) {
                Log.e("rx_test", "distinctUntilChanged(Func1):向前去重:" + house.getCommunityName() + "小區,大小:" + house.getSize());
            }
        });複製程式碼

  輸出結果:

distinctUntilChanged(Func1):向前去重:東方花園小區,大小:105.6
distinctUntilChanged(Func1):向前去重:馬德里春天小區,大小:88.6
distinctUntilChanged(Func1):向前去重:帝豪家園小區,大小:188.7複製程式碼

ElementAt

  elementAt(int index)操作符,獲取觀測序列中第index項索引,並作為唯一資料發射給觀察者,index索引從0開始。

Observable.from(communities)
        .elementAt(1)
        .subscribe(new Action1<Community>() {
            @Override
            public void call(Community community) {
                Log.e("rx_test", "elementAt:第二個小區:" + community.getCommunityName());
            }
        });複製程式碼

  輸出結果:

elementAt:第二個小區:馬德里春天複製程式碼

  原理圖:

First

  1.first()
  只發射觀測序列中的第一個資料項。

Observable.from(communities)
        .first()
        .subscribe(new Action1<Community>() {
            @Override
            public void call(Community community) {
                Log.e("rx_test", "first:" + community.getCommunityName());
            }
        });複製程式碼

  輸出結果:

first:東方花園複製程式碼

  原理圖:

  2.first(Func1)
  根據Func1中call方法的條件,發射符合條件的第一個資料項。

//過濾出第一個小區名為馬德里春天的小區
Observable.from(communities)
        .first(new Func1<Community, Boolean>() {
            @Override
            public Boolean call(Community community) {
                return "馬德里春天".equals(community.getCommunityName());
            }
        })
        .subscribe(new Action1<Community>() {
            @Override
            public void call(Community community) {
                Log.e("rx_test", "first(Func1):" + community.getCommunityName());
            }
        });複製程式碼

  輸出結果:

first(Func1):馬德里春天複製程式碼

Last

  1.last()
  只發射觀測序列中的最後一個資料項。

//傳送最後一個資料項
Observable.from(communities)
        .last()
        .subscribe(new Action1<Community>() {
            @Override
            public void call(Community community) {
                Log.e("rx_test", "last:" + community.getCommunityName());
            }
        });複製程式碼

  輸出結果:

last:帝豪家園複製程式碼

  原理圖:

  2.last(Func1)
  根據Func1中call方法的條件,發射符合條件的最後一個資料項。

//傳送符合條件的最後一個資料項:過濾最後一個小區名為馬德里春天的房源
Observable.from(communities)
        .flatMap(new Func1<Community, Observable<House>>() {
            @Override
            public Observable<House> call(Community community) {
                return Observable.from(community.getHouses());
            }
        })
        .last(new Func1<House, Boolean>() {
            @Override
            public Boolean call(House house) {
                return "馬德里春天".equals(house.getCommunityName());
            }
        })
        .subscribe(new Action1<House>() {
            @Override
            public void call(House house) {
                Log.e("rx_test", "last:" + house.getCommunityName() + "小區,大小:" + house.getSize());
            }
        });複製程式碼

  輸出結果:

last:馬德里春天小區,大小:88.6複製程式碼

總結

  到此,本篇關於RxJava的常用過濾類操作符就講解完畢了,下一篇我們將一起研究RxJava的四類操作符中的組合操作符都有哪些以及如何使用。
  技術渣一枚,有寫的不對的地方歡迎大神們留言指正,有什麼疑惑或者建議也可以在我Github上RxJavaDemo專案Issues中提出,我會及時回覆。
  附上RxJavaDemo的地址:
  RxJavaDemo

相關文章