300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 生怕认可java+flatmap RxJava 操作符flatMap 与 concatMap详解

生怕认可java+flatmap RxJava 操作符flatMap 与 concatMap详解

时间:2020-06-28 03:20:40

相关推荐

生怕认可java+flatmap RxJava 操作符flatMap 与 concatMap详解

本文独家发布到公众号:Android技术杂货铺

封面图-pixabay

近两年来,RxJava可以说是异常的火爆,受到众多开发者的追捧与青睐,虽然后入门的门槛较高,学习成本较大,但是还是掀起一场学习Rxjava的狂潮。为什么呢?因为RxJava的特性:轻松的线程切换、流式的API写法和强大的操作符。这使得我们做异步操作变得很简单,不用像以前一样写各种Handler来回调主线程,只需要一个操作符一行代码就搞定。流式的API使我们的逻辑变得非常清晰,可读性很强。因此,RxJava也是我们项目重构的利器。

说到RxJava强大的操作符,那就不得不提flatMap了,那么篇文章就简单谈谈flatMap的使用场景和它与另一个操作符concatMap的区别。

由于现在RxJava已经发布2.x版本了,因此本文我们使用Rxjava 的 2.x 版本写所有的示例。build.gradle中依赖最新版本:

compile 'io.reactivex.rxjava2:rxjava:2.1.0'

compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

一、操作符 flatMap详解

flatMap将一个发射数据的Observable变换为多个Observables,然后将它们发射的数据合并后放进一个单独的Observable。

flatMap使用一个指定的函数对原始Observable发射的每一项数据之行相应的变换操作,这个函数返回一个本身也发射数据的Observable,然后FlatMap合并这些Observables发射的数据,最后将合并后的结果当做它自己的数据序列发射。

这个方法是很有用的,例如,当你有一个这样的Observable:它发射一个数据序列,这些数据本身包含Observable成员或者可以变换为Observable,因此你可以创建一个新的Observable发射这些次级Observable发射的数据的完整集合。

上面是官方关于flatMap的解释,如果只看这一段话,是不是有点难以理解呢?是的,可能是有点蒙.没关系,我们先来看一个它的使用场景,然后我们倒回来看一下解释,可能你就清楚它是干什么的了。

我们假设有这个一场景:每个学校都有成绩统计系统,有这样一个需求,我们要抽取一个班,打印该班的每个同学的每一门课程成绩。

首先根据需求,抽取2个实体:学生实体和课程实体,如下:

/**

* Created by zhouwei on 17/6/19.

*/

public class Student {

public String name;//学生名字

public int id;

public List mSources;//每个学生的所有课程

public Student(String name, int id, List sources) {

this.name = name;

this.id = id;

mSources = sources;

}

}

课程实体:

public class Source {

public int sourceId;//id

public String name;//课程名

public int score;//成绩

public Source(int sourceId, String name, int score) {

this.sourceId = sourceId;

this.name = name;

this.score = score;

}

}

按照传统的方式:

//开启一个线程

new Thread(new Runnable() {

@Override

public void run() {

// 从服务器获取班级所有同学信息

List students = MockData.getAllStudentInfoById(0);

for(int i=0;i

List sources = students.get(i).mSources;

for (int index=0;index

final Source source = sources.get(index);

runOnUiThread(new Runnable() {

@Override

public void run() {

//主线程更改UI

String content = "sourceName:"+source.name +" source score:"+source.score;

mTextView.setText(content);

Log.i(TAG,content);

}

});

}

}

}

}).start();

以上就是传统的实现方式,可以看到整个代码的结构是非常难看的,如果在加一些其他的过滤条件,要打印指定某一个人的某一门课程的成绩?那么我们又会嵌套if-else判断,这种多层的循环嵌套使得我们的代码可读性变得非常差。怎么解决呢?flatMap操作符就是专门为这个而生的,我们来看一下用flatMap来改造一下:

Flowable.fromIterable(MockData.getAllStudentInfoById(0))

.flatMap(new Function>() {

@Override

public Publisher apply(@NonNull Student student) throws Exception {

return Flowable.fromIterable(student.mSources);

}

})

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(new Consumer() {

@Override

public void accept(@NonNull Source source) throws Exception {

String content = "sourceName:"+source.name +" source score:"+source.score;

mTextView.setText(content);

Log.i(TAG,content);

}

});

这样是不是看起来就舒服多了呢?每一步做了什么我们都看得很清楚,整个流程非常的清晰。

打印的结果是这样如下(3位同学):

那么我们结合这个示例和上面的图我们在回过头来理解一下官方对flatMap的解释

解释:上图中的圆表示的就是原始数据,中间是flatMap操作符执行的变换(将圆变换为一个菱形和正方形),函数返回的是一个可以发射数据(菱形和正方形)的Observable,最合后并这些发送数据的Observable。那么映射到上面的这个示例就是:List< Student > 代表圆,然后`Flowable.fromIterable(student.mSources)`就是flatMap执行的变换操作,返回的就是可以发射Source数据的Observable(本文用的是Flowable),最后合并后的结果就是自己的数据序列( 也就是上图中打印的3个同学的各门成绩)。

这样应该是说清楚了flatMap的作用了吧?如果还没理解,多看几遍图,看图百遍,其义自现。

注意:如果任何一个通过这个flatMap操作产生的单独的Observable调用onError异常终止了,这个Observable自身会立即调用onError并终止。

flatMap的特点:其实从上面的图就可以看出,经过flatMap操作变换后,最后输出的序列有可能是交错的,因为flatMap最后合并结果采用的是merge操作符。如果要想经过变换后,最终输出的序列和原序列一致,那就会用到另外一个操作符,concatMap。

二、操作符 concatMap介绍以及与 flatMap的区别

concatMap操作符的功能和flatMap是非常相似的,只是有一点,concatMap 最终输出的数据序列和原数据序列是一致,它是按顺序链接Observables,而不是合并(flatMap用的是合并)。

我们来看一个例子:Observable 发射5个数据(1,2,3,4,5),然后分别用flatMap和concatMap 对它执行一个变换( *10),然后再输出结果序列。

flatMap:

Observable.fromArray(1,2,3,4,5)

.flatMap(new Function>() {

@Override

public ObservableSource apply(@NonNull Integer integer) throws Exception {

int delay = 0;

if(integer == 3){

delay = 500;//延迟500ms发射

}

return Observable.just(integer *10).delay(delay, TimeUnit.MILLISECONDS);

}

})

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(new Consumer() {

@Override

public void accept(@NonNull Integer integer) throws Exception {

Log.e("zhouwei","accept:"+integer);

}

});

输出结果序列如下图:

为了更真实的模拟,我们将第三个数据延迟500ms发射,我们看到,最终的结果出现了交错。(如果与原序列一致的话应该是:10,20,30,40,50)。

那么我们用同样的代码,将flatMap换成concatMap 看一下:

Observable.fromArray(1,2,3,4,5)

.concatMap(new Function>() {

@Override

public ObservableSource apply(@NonNull Integer integer) throws Exception {

int delay = 0;

if(integer == 3){

delay = 500;//延迟500ms发射

}

return Observable.just(integer *10).delay(delay, TimeUnit.MILLISECONDS);

}

})

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(new Consumer() {

@Override

public void accept(@NonNull Integer integer) throws Exception {

Log.e("zhouwei","accept:"+integer);

}

});

看一下concatMap的结果序列:

可以看到经过concatMap变换后的数据序列 与 原数据序列的顺序是保持一致的。

小结: concatMap和flatMap的功能是一样的, 将一个发射数据的Observable变换为多个Observables,然后将它们发射的数据放进一个单独的Observable。只不过最后合并ObservablesflatMap采用的merge,而concatMap采用的是连接(concat)。总之一句一话,他们的区别在于:concatMap是有序的,flatMap是无序的,concatMap最终输出的顺序与原序列保持一致,而flatMap则不一定,有可能出现交错。

三、总结

flatMap是Rxjava中一个强大的操作符,在实际项目中,应用的场景很多,比如开始列举的化解循环嵌套,还有一种场景在我们实际项目中是非常多的,那就是连续请求两个接口,第一个接口的返回值是第二个接口的请求参数,在这种情况下,以前我们会在一个请求完成后,在onResponse中获取结果再请求另一个接口。这种接口嵌套,代码看起来是非常丑陋的,运用flatMap就能很好的解决这个问题。代码看起来非常优雅而且逻辑清晰。 如果需要保证顺序的话,请使用concatMap。

以上就是concatMap和flatMap 使用场景介绍及区别与联系,如有什么问题,欢迎指正。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。