Android :RxJava学习笔记之转换操作符
Posted JMW1407
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android :RxJava学习笔记之转换操作符相关的知识,希望对你有一定的参考价值。
RxJava ——转换操作符
转换操作符
转换类操作符顾名思义,就是将上游(被观察者)发送的数据流进行转换成另外一种形式(观察者希望接收到的形式),从而使下游(观察者)能够正确的接受并作出响应。
这里介绍map,cast,flatMap,concatMap,flatMapIterable,switchMap,scan,buffer,window,groupBy
进行讲解
Student类
public class Student {
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
1、map
map的方法声明为:Observable<R> map(Func1<? super T, ? extends R> func)
,其中Func1<T, R>
接口里面只包含一个R call(T t)
方法,从参数和返回值就可以看出来这是一个将T
类型转换成一个R
类型的方法。FuncX
类的接口表示有X+1
个泛型,前X
个是call
方法的参数,最后一个是call方法的返回值。
所以map的作用就是将接收类型为T
的Observable
转换为接收类型为R
的Observable
Map的使用也很简单,比如说现在有一个Student列表,上游发出的数据流是Student格式,但是下游希望接收到的格式是Student对象的成绩(Integer格式),代码如下:
List<Student> studentList = new ArrayList<>();
studentList.add(new Student("U1111", 59));
studentList.add(new Student("U1112", 69));
studentList.add(new Student("U1113", 37));
studentList.add(new Student("U1114", 72));
studentList.add(new Student("U1115", 26));
studentList.add(new Student("U1116", 98));
studentList.add(new Student("U1117", 53));
studentList.add(new Student("U1118", 60));
studentList.add(new Student("U1119", 100));
Observable
.fromIterable(studentList)
.map(new Function<Student, Integer>() {
@Override
public Integer apply(Student student) throws Exception {
return student.getScore();
}
})
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
System.out.println("成绩为:" + integer);
}
});
输出
成绩为:59
成绩为:69
成绩为:37
成绩为:72
成绩为:26
成绩为:98
成绩为:53
成绩为:60
成绩为:100
上述代码可以理解为from传入数据流后,经过map函数,转换成Integer,再到达subscriber,调用事件回调函数。
2、cast
cast运算符作用和map类似,但是它只能将父类对象转换为子类对象,是map的特殊版本。
方法声明:Observable<R> cast(final Class<R> klass)
,将源事件类型转换成klass的数据类型。
- 注意只能将父类对象转为子类对象
- cast执行转化必须是可以转的,如果该父类对象无法转为子类对象,会抛出
ClassCastException
异常
Person是Student的父类:
List<Person> personList = new ArrayList<>(studentList);
Observable.from(personList)
.cast(Student.class)
.subscribe(student -> System.out.println(student.getId()));
3、flatMap
现在比如给出一个年级的所有班级的列表ArrayList,每个IClass对象都包含内部学生的列表ArrayList,我要输出每个班人的成绩。我当然可以这样做:
Observable.from(iClassList)
.subscribe(new Action1<IClass>() {
@Override
public void call(IClass iClass) {
for (Student student : iClass.getStudentList()) {
System.out.println(student.getGrade());
}
}
});
那么如果我不想在事件回调的时候去遍历,我就想让事件回调时接受到的就是每个Student,而不是一个班级IClass,该如何做呢?
这个时候就需要用到flatMap这个操作符了,先看看具体的实现:
Observable.from(iClassList)
.flatMap(new Func1<IClass, Observable<Student>>() {
@Override
public Observable<Student> call(IClass iClass) {
return Observable.from(iClass.getStudentList());
}
})
.subscribe(new Action1<Student>() {
@Override
public void call(Student student) {
System.out.println(student.getGrade());
}
});
flatMap的参数形式为Observable<R> flatMap(Func1<? super T, ? extends Observable<? extends R>> func)
,传入一个T类型,返回一个接收事件为R
类型的Observable
,在结合上面的例子,就很好理解flatMap的作用了。
上述代码调用流程:
flatMap
依次将传入的每一个iClass
对象转换为一个Observable<Student>
对象,在这个Observable
中传入iClass.getStudentList()
事件列表- 最后将所有
Observable<Student>
对象中的事件都汇集到一个Observable
之中,这个Obsevable
就是flatMap
的返回值,由这个Observable
将所有的事件发给Subscriber
,Subscriber再对每个事件依次调用事件回到函数。
flatMap
的缺点在于它并不一定会按照原始Observable的顺序发送事件,可能会有事件的交叉发送的情况。
原理图如下:
4、concatMap
上面也讲到了flatMap的缺点,那么如何解决交叉的问题呢?RxJava给我们提供了concatMap这个方法,它用来将最初发送的事件按照原来的顺序连续发送出去。使用方法跟flatMap一样。
Observable<R> concatMap(Func1<? super T, ? extends Observable<? extends R>> func)
4.1、concatMap和flatmap对比
concatMap操作符的功能和flatMap是非常相似的,只是有一点,concatMap 最终输出的数据序列和原数据序列是一致,它是按顺序链接Observables,而不是合并(flatMap用的是合并)。
我们来看一个例子:Observable 发射5个数据(1,2,3,4,5),然后分别用flatMap和concatMap 对它执行一个变换( *10),然后再输出结果序列。
Observable.fromArray(1,2,3,4,5)
.flatMap(new Function<Integer, ObservableSource<Integer>>() {
@Override
public ObservableSource<Integer> 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(Schedulers.newThread())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
System.out.println("accept:"+integer);
}
});
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
为了更真实的模拟,我们将第三个数据延迟500ms发射,我们看到,最终的结果出现了交错。(如果与原序列一致的话应该是:10,20,30,40,50)。
输出
accept:10
accept:20
accept:40
accept:50
accept:30
那么我们用同样的代码,将flatMap换成concatMap 看一下:
Observable.fromArray(1,2,3,4,5)
.concatMap((Function<Integer, ObservableSource<Integer>>) integer -> {
int delay = 0;
if(integer == 3){
delay = 500;//延迟500ms发射
}
return Observable.just(integer *10).delay(delay, TimeUnit.MILLISECONDS);
})
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.subscribe(integer -> System.out.println("accept:"+integer));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
输出
accept:10
accept:20
accept:30
accept:40
accept:50
可以看到经过concatMap变换后的数据序列 与 原数据序列的顺序是保持一致的。
小结:
concatMap和flatMap的功能是一样的, 将一个发射数据的Observable变换为多个Observables,然后将它们发射的数据放进一个单独的Observable。只不过最后合并ObservablesflatMap采用的merge,而concatMap采用的是连接(concat)。总之一句一话,他们的区别在于:concatMap是有序的,flatMap是无序的,concatMap最终输出的顺序与原序列保持一致,而flatMap则不一定,有可能出现交错。
5、flatMapIterable
flatMapIterable跟flatMap相差无几,区别在于flatMapIterable转换后的是一个Iterable对象,最后将所有的Iterable都传入一个Observable中,由这个Observable来将事件发送出去。
原理图如下:
Observable.from(iClassList)
.flatMapIterable(new Func1<IClass, Iterable<Student>>() {
@Override
public Iterable<Student> call(IClass iClass) {
return iClass.getStudentList();
}
})
.subscribe(new Action1<Student>() {
@Override
public void call(Student student) {
System.out.println(student.getGrade());
}
});
6、scan
scan就是将前一个事件产生的结果发送到下一个事件转换时的第一个参数使用。
Observable<T> scan(Func2<T, T, T> accumulator)
Observable<R> scan(R initialValue, Func2<R, ? super T, R> accumulator)
Func2接口的三个泛型,前两个是内部call方法的调用参数,第三个是call的返回值
先说第一个重载,第一个T是前一个事件经过Func2.call方法后的返回值,第二个T是当前事件,第三个T是调用call方法后的返回值,作为下一个事件的Func2的第一个参数,至于第一个事件就是默认无初始计算结果了。第二个重载就是给第一个事件设置一个初始的计算结果。
代码如下:
Observable
.just(1, 2, 3, 4, 5)
.scan(new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) throws Exception {
return integer + integer2;
}
})
.doOnComplete(new Action() {
@Override
public void run() throws Exception {
System.out.println("Sequence complete");
}
})
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
System.out.println("Success: " + integer);
}
}
);
}
简化
Observable
.just(1, 2, 3, 4, 5)
.scan((integer, integer2) -> integer + integer2)
.doOnComplete(() -> System.out.println("Sequence complete"))
.subscribe(integer -> System.out.println("Success: " + integer)
);
输出
Success: 1
Success: 3
Success: 6
Success: 10
Success: 15
Sequence complete
7、buffer
buffer就是将原有的事件转换成一个List列表,由一个新的Observable一次性发送出去,方法声明如下:
Observable<List<T>> buffer(int count)
Observable<List<T>> buffer(int count, int skip)
第一种重载内部就是调用了buffer(count, count)
,所以就直接分析第二个重载。
count
和skip
的含义为:
- 把每skip个事件中的前count个事件转换成一个List列表,剩余的丢弃,再由新的Observable将这个列表发送出去。
- 比如现在有一个StudentList,id分别是从U1001到U1008,总共八个数据,如果调用如下代码:
List<Student> studentList = new ArrayList<>();
studentList.add(new Student("U1111", 59));
studentList.add(new Student("U1112", 69));
studentList.add(new Student("U1113", 37));
studentList.add(new Student("U1114", 72));
studentList.add(new Student("U1115", 26));
studentList.add(new Student("U1116", 98));
studentList.add(new Student("U1117", 53));
studentList.add(new Student("U1118", 60));
studentList.add(new Student("U1119", 100));
Observable
.fromIterable(studentList)
.buffer(4, 5)
.subscribe(new Consumer<List<Student>>() {
@Override
public void accept(List<Student> students) throws Exception {
for(Student student : students){
System.out.println(student.getName());
}
}
});
}
简化
Observable
.fromIterable(studentList)
.buffer(4, 5)
.subscribe(students -> {
for(Student student : students){
System.out.println(student.getName());
}
});
输出
U1111
U1112
U1113
U1114
U1116
U1117
U1118
U1119
U1005是第一组5个中的第5个被丢弃了,U1006-U1008是第二组5个中的前三个,由于第4个不存在,所以发送第三个后就结束发送了。
8、window
window与buffer的作用非常相似,区别在于buffer发送出去的是一个列表List,而window发送出去的事件是一个Observable对象
,方法声明如下:
Observable<Observable<T>> window(int count)
Observable<Observable<T>> window(int count, int skip)
所以在window的subscribe中得到的是一个Observable对象,需要再次调用subscribe方法,去获取其中的数据,代码如下:
List<Student> studentList = new ArrayList<>();
studentList.add(new Student("U1111", 59));
studentList.add(new Student("U1112", 69));
studentList.add(new Student("U1113", 37));
studentList.add(new Student("U1114", 72));
studentList.add(new Student("U1115", 26));
studentList.add(new Student("U1116", 98));
studentList.add(new Student("U1117", 53));
studentList.add(new Student("U1118", 60));
studentList.add(new Student("U1119", 100));
Observable
.fromIterable(studentList)
.window(3, 5)
.subscribe(
studentObservable -> studentObservable.subscribe(
student -> System.out.println(student.getName())));
输出
U1111
U1112
U1113
U1116
U1117
U1118
9、groupBy
groupBy的常用调用方式如下:
Observable<GroupedObservable<K, T>> groupBy(final Func1<? super T, ? extends K> keySelector)
groupBy
通过keySelector
将发送的事件分为不同的组,并将每一组以Observable<GroupObserverable>
的形式发送出去。
keySelector
第一个参数为call
方法传入的事件类型,第二个参数是返回的key
比如发出的事件源为学生列表,但是我想将其分为及格的和不及格的分别处理,代码如下
List<Student> studentList = new ArrayList<>();
studentList.add(new Student("U1111", 59));
studentList.add(new Student("U1112", 69));
studentList.add(new Student("U1113", 37));
studentList.add(new Student("U1114", 72));
studentList.add(new Student("U1115", 26));
studentList.add(new Student("U1116", 98));
studentList.add(new Student("U1117", 53));
studentList.add(new Student("U1118", 60));
studentList.add(new Student("U1119", 100));
Observable
.fromIterable(studentList)
.groupBy(new Function<Student, Integer>() {
@Override
public Integer apply(Student student) throws Exception {
return student.getScore() >= 60 ? 1 : 0;
}
})
.subscribe(new Consumer<GroupedObservable<Integer, Student>>() {
@Override
public void accept(GroupedObservable<Integer, Student> integerStudentGroupedObservable)
throws Exception {
integerStudentGroupedObservable.subscribe(new Consumer<Student>() {
@Override
public void accept(Student student) throws Exception {
if(integerStudentGroupedObservable.getKey() == 0){
System.out.println("不及格ID: " + student.getName());
} else {
System.out.println("及格ID: " + student.getName());
}
}
});
}
});
简化:
Observable
.fromIterable(studentList)
.groupBy(student -> student.getScore() >= 60 ? 1 : 0)
.subscribe(integerStudentGroupedObservable -> integerStudentGroupedObservable.subscribe(
student -> {
if(integerStudentGroupedObservable.getKey() == 0){
System.out.println("不及格ID: " + student.getName());
} else {
System.out.println("及格ID: " + student.getName());
}
}));
输出
不及格ID: U1111
及格ID: U1112
不及格ID: U1113
及格ID: U1114
不及格ID: U1115
及格ID: U1116
不及格ID: U1117
及格ID: U1118
及格ID: U1119
参考
1、https://blog.csdn.net/boyeleven/article/details/81867256
以上是关于Android :RxJava学习笔记之转换操作符的主要内容,如果未能解决你的问题,请参考以下文章