本文参考:https://www.jianshu.com/p/128e662906af
3.5 Map与FlatMap
Map与FlatMap是RxJava中的操作符。
那什么是操作符?
RxJava中的操作符就是为了提供函数式的特性,函数式最大的好处就是使得数据处理简洁易懂。操作符实质上就是RxJava函数式编程模式的体现。在我看来,函数就是变换关系的简称,比如在有一个数字集合A,又有一个数字集合B,从数字集合A变换到数字集合B的的这种关系,可以将其称为函数。
先看一下Map操作符。map是RxJava中最简单的一个变换操作符了,它的作用就是对上游发送的每一个事件应用一个函数,使得每一个事件都按照指定的函数去变化。用事件图表示如下:
图中的map函数将圆形事件变成了矩阵事件,在代码中,我们将int类型转变为String类型。
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onComplete();
}
}).map(new Function<Integer, String>() {
@Override
public String apply(Integer integer) throws Exception {
return "这是:" + integer;
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.i(TAG, s);
}
});
打印出的Log如下所示:
2019-04-25 17:15:07.747 30351-30351/? I/RxJavaTestHaHa: 这是:1
2019-04-25 17:15:07.747 30351-30351/? I/RxJavaTestHaHa: 这是:2
2019-04-25 17:15:07.747 30351-30351/? I/RxJavaTestHaHa: 这是:3
可以看到,我们在Observable中输入的是int类型,但是到了Consumer上,却变为了String类型,起作用的就是中间的map函数,它将String类型变为了int类型。
接下来是FlatMap操作符,这是一个很强大的操作符。
FlatMap将一个发送事件的上游的Observable,变为多个发送事件的Observable,然后将它们发射的事件合并后放进一个单独的Observable中。
形象的解释如下图所示,我们将一个圆事件变成正方形事件和三角形事件。
从上面两张图可以看到的是,上游的Observable每发送一个事件,faltMap都会创建一个新的水管,发送转换之后的事件,但是flatMap并不保证转换后事件的顺序性,并不能一定保证事件1在事件2的前面,如果需要顺序保证可以使用concatMap。
flatMap的示例代码如下所示:
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}
}).flatMap(new Function<Integer, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(Integer integer) throws Exception {
final List<String> list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
list.add("Value is:" + integer + " " + i);
}
return Observable.fromIterable(list).delay(10, TimeUnit.MILLISECONDS);
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.i(TAG, "accept: " + s);
}
});
打印出的Log如下所示,从log中可以看到,flatMap是不能保证顺序的。
2019-04-25 19:53:58.070 16442-16480/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:2 0
2019-04-25 19:53:58.070 16442-16480/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:2 1
2019-04-25 19:53:58.071 16442-16480/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:2 2
2019-04-25 19:53:58.071 16442-16481/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:3 0
2019-04-25 19:53:58.071 16442-16481/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:1 0
2019-04-25 19:53:58.071 16442-16481/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:1 1
2019-04-25 19:53:58.071 16442-16481/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:1 2
2019-04-25 19:53:58.071 16442-16481/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:3 1
2019-04-25 19:53:58.071 16442-16481/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:3 2
当时将代码改成下面这样,只是在Log中少打印i,但是打印出来的字符串却会发生变化。
代码如下所示:
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}
}).flatMap(new Function<Integer, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(Integer integer) throws Exception {
final List<String> list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
list.add("Value is:" + integer); //只是将这一行发生变换
}
return Observable.fromIterable(list).delay(10, TimeUnit.MILLISECONDS);
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.i(TAG, "accept: " + s);
}
});
打印出的Log则会变成如下这样:
2019-04-25 20:01:03.096 18724-18765/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:1
2019-04-25 20:01:03.096 18724-18765/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:1
2019-04-25 20:01:03.096 18724-18766/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:2
2019-04-25 20:01:03.096 18724-18766/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:2
2019-04-25 20:01:03.097 18724-18767/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:3
2019-04-25 20:01:03.097 18724-18767/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:3
2019-04-25 20:05:47.116 20344-20406/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:1
2019-04-25 20:05:47.116 20344-20406/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:1
2019-04-25 20:05:47.117 20344-20407/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:2
2019-04-25 20:05:47.117 20344-20408/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:3
2019-04-25 20:05:47.117 20344-20408/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:3
2019-04-25 20:05:47.117 20344-20407/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:2
2019-04-25 20:05:47.117 20344-20407/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:2
可以看到,打印出来的有的String漏掉了,为什么会变成这样,我也不是很清楚,先留个Todo在这里,以后解决一下这个问题。
flatMap写完之后也说一下前面提过的concatMap,它和flatMap几乎是一模一的,只不过concatMap是严格按照上游发送的顺序来发送的。
代码如下所示:
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}
}).concatMap(new Function<Integer, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(Integer integer) throws Exception {
List<String> stringList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
stringList.add("Value is" + integer + " " + i);
}
return Observable.fromIterable(stringList).delay(10, TimeUnit.MILLISECONDS);
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.i(TAG, "accept: " + s);
}
});
上面提到的问题在下面依然存在,先看日志吧:
2019-04-25 20:16:59.878 22597-22655/? I/RxJavaTestHaHa: accept: Value is1 0
2019-04-25 20:16:59.878 22597-22655/? I/RxJavaTestHaHa: accept: Value is1 1
2019-04-25 20:16:59.878 22597-22655/? I/RxJavaTestHaHa: accept: Value is1 2
2019-04-25 20:16:59.888 22597-22659/? I/RxJavaTestHaHa: accept: Value is2 0
2019-04-25 20:16:59.889 22597-22659/? I/RxJavaTestHaHa: accept: Value is2 1
2019-04-25 20:16:59.889 22597-22659/? I/RxJavaTestHaHa: accept: Value is2 2
2019-04-25 20:16:59.901 22597-22662/? I/RxJavaTestHaHa: accept: Value is3 0
2019-04-25 20:16:59.902 22597-22662/? I/RxJavaTestHaHa: accept: Value is3 1
2019-04-25 20:16:59.902 22597-22662/? I/RxJavaTestHaHa: accept: Value is3 2
接下来是抓虫子的时间段了。
我将代码改成了下面这样,只是多了几条Log。
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}
}).flatMap(new Function<Integer, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(Integer integer) throws Exception {
Log.i(TAG, "apply: interger is " + integer);
List<String> list = new ArrayList<String>();
for (int i = 0; i < 3; i++) {
list.add("Value is:" + integer);
}
Log.i(TAG, "apply: Size is " + list.size());
for (String s : list) {
Log.i(TAG, s);
}
return Observable.fromIterable(list).delay(10, TimeUnit.MILLISECONDS);
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.i(TAG, "accept: " + s);
}
});
Log如下所示:
2019-04-25 21:25:06.553 6670-6670/com.xupt.will.rxjavatest I/RxJavaTestHaHa: apply: interger is 1
2019-04-25 21:25:06.553 6670-6670/com.xupt.will.rxjavatest I/RxJavaTestHaHa: apply: Size is 3
2019-04-25 21:25:06.553 6670-6670/com.xupt.will.rxjavatest I/RxJavaTestHaHa: Value is:1
2019-04-25 21:25:06.553 6670-6670/com.xupt.will.rxjavatest I/RxJavaTestHaHa: Value is:1
2019-04-25 21:25:06.557 6670-6670/com.xupt.will.rxjavatest I/RxJavaTestHaHa: apply: interger is 2
2019-04-25 21:25:06.558 6670-6670/com.xupt.will.rxjavatest I/RxJavaTestHaHa: apply: Size is 3
2019-04-25 21:25:06.558 6670-6670/com.xupt.will.rxjavatest I/RxJavaTestHaHa: Value is:2
2019-04-25 21:25:06.558 6670-6670/com.xupt.will.rxjavatest I/RxJavaTestHaHa: Value is:2
2019-04-25 21:25:06.558 6670-6670/com.xupt.will.rxjavatest I/RxJavaTestHaHa: apply: interger is 3
2019-04-25 21:25:06.558 6670-6670/com.xupt.will.rxjavatest I/RxJavaTestHaHa: apply: Size is 3
2019-04-25 21:25:06.558 6670-6670/com.xupt.will.rxjavatest I/RxJavaTestHaHa: Value is:3
2019-04-25 21:25:06.558 6670-6670/com.xupt.will.rxjavatest I/RxJavaTestHaHa: Value is:3
2019-04-25 21:25:06.567 6670-6739/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:1
2019-04-25 21:25:06.568 6670-6739/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:1
2019-04-25 21:25:06.568 6670-6740/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:2
2019-04-25 21:25:06.568 6670-6740/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:2
2019-04-25 21:25:06.568 6670-6741/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:3
2019-04-25 21:25:06.569 6670-6741/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: Value is:3
在打印List的大小时还是正确的,但不知道为什么在遍历时出现这样的错误。
额,断点调式查看List没有问题呀。Log与RxJava命数相冲?我在网上查阅资料时说RxJava在Android8.0 与Android9.0上不兼容,有可能是这个问题,不管怎么样,大体上RxJava还是正确的。