RxJava(三)转换操作符
- RxJava(一)概述与基本使用
- RxJava(二)创建操作符
- RxJava(三)转换操作符
上一篇博客已经讲解了RxJava的API的基本使用,这里我们来学习下RxJava的另一个特性——对数据流的转换操作符。
RxJava中提供的操作符可以分为转换类操作符,过滤类操作符以及组合类操作符,接下来我将依次对这三种操作符的使用进行讲解。
一、转换类操作符
转换类操作符顾名思义,就是将上游(被观察者)发送的数据流进行转换成另外一种形式(观察者希望接收到的形式),从而使下游(观察者)能够正确的接受并作出响应。
这里介绍map
,cast
,flatMap
,concatMap
,flatMapIterable
,switchMap
,scan
,buffer
,window
,groupBy
进行讲解
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
RxJava官方给出的Map原理图:

Map的使用也很简单,比如说现在有一个Student列表,上游发出的数据流是Student格式,但是下游希望接收到的格式是Student对象的成绩(Integer格式),代码如下:
Observable.from(studentList)
.map(new Func1<Student, Integer>() {
@Override
public Integer call(Student student) {
return student.getGrade();
}
})
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
System.out.println("成绩为:" + integer);
}
});
上述代码可以理解为from
传入数据流后,经过map
函数,转换成Integer
,再到达subscriber
,调用事件回调函数。
cast
cast
运算符作用和map
类似,但是它只能将父类对象转换为子类对象,是map
的特殊版本。
方法声明:Observable<R> cast(final Class<R> klass)
,将源事件类型转换成klass
的数据类型,注意只能将父类对象转为子类对象
Person
是Student
的父类:
List<Person> personList = new ArrayList<>(studentList);
Observable.from(personList)
.cast(Student.class)
.subscribe(student -> System.out.println(student.getId()));
cast
执行转化必须是可以转的,如果该父类对象无法转为子类对象,会抛出ClassCastException
异常
原理图如下:

flatMap
现在比如给出一个年级的所有班级的列表ArrayList<IClass>
,每个IClass
对象都包含内部学生的列表ArrayList<Student>
,我要输出每个班人的成绩。我当然可以这样做:
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
的顺序发送事件,可能会有事件的交叉发送的情况。
原理图如下:

concatMap
上面也讲到了flatMap
的缺点,那么如何解决交叉的问题呢?RxJava给我们提供了concatMap
这个方法,它用来将最初发送的事件按照原来的顺序连续发送出去。使用方法跟flatMap
一样。
Observable<R> concatMap(Func1<? super T, ? extends Observable<? extends R>> func)
原理图如下:

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());
}
});
switchMap
switchMap
的作用也和flatMap
相似,但是它在连续发送事件的时候如果前一个事件未订阅完成,后一个事件就已经到达,此时会将前一个事件直接取消,可多用于网络请求之中。
Observable.just("first", "second", "third", "fourth", "fifth")
.switchMap(new Func1<String, Observable<String>>() {
@Override
public Observable<String> call(String s) {
return Observable.just(s).subscribeOn(Schedulers.newThread());
}
})
.subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {
System.out.println("success");
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(String s) {
System.out.println(s);
}
});
上述代码每次产生事件都是新开一个线程,这样有可能前面的事件在订阅未完成时,后面的事件已经到来,这样就会取消前一个事件,这种情况只有在并发的情况下才会发生,同一个线程事件先后发送执行是不会出现这种情况的。至于最后出现的结果,由于线程的调用取决于CPU的调度,所以会出现不同的情况,详情可以参看RxJava的源码分析。
原理图如下:

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 Func2<Integer, Integer, Integer>() {
@Override
public Integer call(Integer sum, Integer item) {
return sum + item;
}
}).subscribe(new Subscriber<Integer>() {
@Override
public void onNext(Integer item) {
System.out.println("Next: " + item);
}
@Override
public void onError(Throwable error) {
System.err.println("Error: " + error.getMessage());
}
@Override
public void onCompleted() {
System.out.println("Sequence complete.");
}
});
结果输出为:
Next: 1
Next: 3
Next: 6
Next: 10
Next: 15
Sequence complete.
原理图:

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,总共八个数据,如果调用如下代码:
Observable.from(studentList)
.buffer(3, 4)
.subscribe(studentList1 -> {
for (Student student : studentList1) {
System.out.println(student.getId());
}
});
结果为:
U1001
U1002
U1003
U1005
U1006
U1007
可见U1004和U1008被丢弃了,如果调用buffer(4,5)
,结果如下:
U1001
U1002
U1003
U1004
U1006
U1007
U1008
U1005是第一组5个中的第5个被丢弃了,U1006-U1008是第二组5个中的前三个,由于第4个不存在,所以发送第三个后就结束发送了。
原理图如下:

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
方法,去获取其中的数据,代码如下:
Observable.from(studentList)
.window(3, 5)
.subscribe(observable -> observable.subscribe(student -> System.out
.println(student.getId())));
以上是lambda表达式的写法,不清楚的可以去学习一下Lambda表达式。
原理图如下:
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.from(studentList)
.groupBy(new Func1<Student, Integer>() {
@Override
public Integer call(Student student) {
return student.getGrade() >= 60 ? 1 : 0;
}
})
.subscribe(new Action1<GroupedObservable<Integer, Student>>() {
@Override
public void call(GroupedObservable<Integer, Student> integerStudentGroupedObservable) {
integerStudentGroupedObservable.subscribe(new Action1<Student>() {
@Override
public void call(Student student) {
if (integerStudentGroupedObservable.getKey() == 0) {
System.out.println("不及格的ID为" + student.getId());
} else {
System.out.println("及格的ID为" + student.getId());
}
}
});
}
});
运行代码结果为:
不及格的ID为U1111
及格的ID为U1112
不及格的ID为U1113
及格的ID为U1114
不及格的ID为U1115
及格的ID为U1116
不及格的ID为U1117
及格的ID为U1118
及格的ID为U1119
原理图如下:
