RxJava操作符详解(四)
七、条件操作符
| 操作符 | 描述 |
|---|---|
all |
判断是否所有发射的数据都满足指定条件 |
amb |
在多个Observable中选择第一个发射数据的Observable |
contains |
检查流中是否包含指定元素 |
isEmpty |
检查流是否为空 |
defaultIfempty |
如果源Observable为空,发射一个默认值 |
switchIfEmpty |
如果源Observable为空,切换到另一个Observable |
sequenceEqual |
比较两个Observable发射的数据序列是否相同 |
skipUntil |
跳过源Observable的数据,直到另一个Observable开始发射 |
takeUntil |
获取源Observable的数据,直到另一个Observable开始发射 |
skipWhile |
当条件为true时跳过,条件为false时开始接收 |
takeWhile |
当条件为true时接收,条件为false时停止 |
7.1、all
al操作符根据一个函数对源Observable发送的所有数据进行判断,最终返回的结果就是这个判断结果。这个函数使用源Observable发送的数据作为参数,内部判断所有的数据是否满足我们定义好的判断条件,如果全部都满足则返回true,否则就返回false,其示意图如图所示。

下面创建两个Observable,.一个发送15的整数,另一个发送16的整数,然后使用all操作符进行判断,如代码所示。`
java
@Test
public void testAll() {
Observable.just(1, 2, 3, 4, 5)
.all(i -> i < 6)
.subscribe(e -> System.out.println("all:" + e));
Observable.just(1, 2, 3, 4, 5, 6)
.all(i -> i < 6)
.subscribe(e -> System.out.println("not all:" + e));
}
all:true
not all:false
7.2、amb
amb是 "ambiguous"(模糊的)的缩写,用于在多个Observable中选择第一个发射数据的Observable,忽略其他所有Observable。
amb操作符可以将至多9个Observable结合起来,让它们竞争。哪个Observable首先发送了数据(包括onError和onComplete),就继续发送这个Observable的数据,其他Observable所发送的数据都会被丢弃,其示意图如图所示。

7.2.1、操作符变体
Observable.amb():接收一个Observable的可迭代集合
java
@Test
public void testAmb() {
List<Observable<String>> observables = Arrays.asList(
Observable.timer(2, TimeUnit.SECONDS).map(i -> "Source 1"),
Observable.timer(1, TimeUnit.SECONDS).map(i -> "Source 2"),
Observable.timer(3, TimeUnit.SECONDS).map(i -> "Source 3")
);
Observable.amb(observables)
.subscribe(
data -> System.out.println("Received: " + data),
error -> System.out.println("Error: " + error),
() -> System.out.println("Completed")
);
// 输出: Received: Source 2
// 只有第二个Observable的数据会被接收
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Received: Source 2
Completed
Observable.ambArray():接收可变参数的Observable数组
java
Observable<String> source1 = Observable.timer(500, TimeUnit.MILLISECONDS)
.map(i -> "Fast Source (500ms)");
Observable<String> source2 = Observable.timer(200, TimeUnit.MILLISECONDS)
.map(i -> "Faster Source (200ms)");
Observable<String> source3 = Observable.timer(1000, TimeUnit.MILLISECONDS)
.map(i -> "Slow Source (1000ms)");
Observable.ambArray(source1, source2, source3)
.subscribe(data -> System.out.println("Winner: " + data));
// 输出: Winner: Faster Source (200ms)
ambWith():实例方法,用于与另一个Observable竞争
java
Observable<String> mainSource = Observable.create(emitter -> {
new Thread(() -> {
Thread.sleep(300);
emitter.onNext("Main Source");
emitter.onComplete();
}).start();
});
Observable<String> backupSource = Observable.create(emitter -> {
new Thread(() -> {
Thread.sleep(200);
emitter.onNext("Backup Source");
emitter.onComplete();
}).start();
});
mainSource.ambWith(backupSource)
.subscribe(winner -> System.out.println("Selected: " + winner));
// 输出: Selected: Backup Source
7.2.2、不同类型Observable的行为
1. 立即发射的Observable
java
Observable<String> immediate = Observable.just("Immediate");
Observable<String> delayed = Observable.timer(1, TimeUnit.SECONDS)
.map(i -> "Delayed");
Observable.ambArray(immediate, delayed)
.subscribe(System.out::println); // 输出: Immediate
2. 空的Observable
java
// 空Observable永远不会被选择,因为它从不发射
Observable<String> empty = Observable.empty();
Observable<String> hasData = Observable.just("Has Data");
Observable.ambArray(empty, hasData)
.subscribe(System.out::println); // 输出: Has Data
3. 立即结束的Observable(empty/error)
java
Observable<String> immediateError = Observable.error(
new RuntimeException("Error immediately")
);
Observable<String> normalSource = Observable.timer(100, TimeUnit.MILLISECONDS)
.map(i -> "Normal Data");
Observable.ambArray(immediateError, normalSource)
.subscribe(
data -> System.out.println("Data: " + data),
error -> System.out.println("Error caught: " + error.getMessage())
);
// 输出: Error caught: Error immediately
// 错误会被立即传播
7.2.3、实际应用场景
1. 多数据源竞速(冗余请求)
java
public Observable<Response> loadDataWithRedundancy() {
// 多个相同功能的数据源
Observable<Response> source1 = apiService.getDataFromPrimary()
.subscribeOn(Schedulers.io());
Observable<Response> source2 = apiService.getDataFromBackup1()
.subscribeOn(Schedulers.io());
Observable<Response> source3 = apiService.getDataFromBackup2()
.subscribeOn(Schedulers.io());
return Observable.ambArray(source1, source2, source3)
.timeout(3, TimeUnit.SECONDS, Observable.error(new TimeoutException()));
}
2. 缓存优先策略
java
public Observable<Data> loadDataWithCacheFirst() {
Observable<Data> memoryCache = loadFromMemoryCache();
Observable<Data> diskCache = loadFromDiskCache();
Observable<Data> network = loadFromNetwork();
return Observable.ambArray(
memoryCache, // 内存最快
diskCache, // 其次磁盘
network // 最后网络
).firstElement()
.toObservable();
}
3. 服务健康检查
java
public Observable<Server> findHealthyServer(List<Server> servers) {
List<Observable<Server>> healthChecks = servers.stream()
.map(server -> checkServerHealth(server)
.filter(healthy -> healthy)
.map(healthy -> server)
.timeout(2, TimeUnit.SECONDS, Observable.empty())
)
.collect(Collectors.toList());
return Observable.amb(healthChecks)
.firstElement()
.toObservable();
}
private Observable<Boolean> checkServerHealth(Server server) {
return apiClient.ping(server)
.map(response -> response.isSuccessful())
.onErrorReturnItem(false)
.subscribeOn(Schedulers.io());
}
7.2.4、高级用法和模式
1. 带标签的数据源
java
class SourceResult {
final String source;
final String data;
SourceResult(String source, String data) {
this.source = source;
this.data = data;
}
}
public Observable<SourceResult> loadFromFastestSource() {
Observable<SourceResult> sourceA = apiA.getData()
.map(data -> new SourceResult("API_A", data))
.subscribeOn(Schedulers.io());
Observable<SourceResult> sourceB = apiB.getData()
.map(data -> new SourceResult("API_B", data))
.subscribeOn(Schedulers.io());
Observable<SourceResult> sourceC = cache.getData()
.map(data -> new SourceResult("CACHE", data))
.subscribeOn(Schedulers.io());
return Observable.ambArray(sourceA, sourceB, sourceC);
}
2. 组合使用其他操作符
java
Observable.ambArray(
apiCall1.timeout(2, TimeUnit.SECONDS),
apiCall2.timeout(2, TimeUnit.SECONDS),
Observable.just("Fallback").delay(3, TimeUnit.SECONDS)
)
.filter(data -> !data.isEmpty())
.firstElement()
.toObservable()
.subscribe(
data -> System.out.println("Got: " + data),
error -> System.out.println("All failed"),
() -> System.out.println("Completed")
);
3. 动态构建amb
java
public Observable<String> raceConditions(List<Supplier<Observable<String>>> suppliers) {
List<Observable<String>> observables = suppliers.stream()
.map(supplier -> Observable.fromCallable(() -> supplier.get())
.flatMap(obs -> obs)
.subscribeOn(Schedulers.io())
)
.collect(Collectors.toList());
return Observable.amb(observables);
}
7.3、contains
contains操作符用来判断源Observable所发送的所有数据是否包含某一个数据,如果包含则返回true;如果源Observable已经结束了却还没有发送这个数据,则返回false。所以在
Observable没发送完所有的数据之前,contains是不会有返回数据的,其示意图如图所示。

创建两个相同的Observable,.发送1~3的整数,然后分别用contains操作符判断它们所发送的数据里是否包含3和4,如代码所示。
java
@Test
public void testContains() {
Observable.just(1, 2, 3)
.contains(3)
.subscribe(e -> System.out.println("contains:" + e));
Observable.just(1, 2, 3)
.contains(4)
.subscribe(e -> System.out.println("notcontains:" + e));
}
contains:true
notcontains:false
7.4、isEmpty
isEmpty操作符用来判断源Observable是否发送过数据,如果发送过就会返回false;如果源Observable已经结束了都还没有发送这个数据,则返回true,其示意图如图所示。

创建一个Observable,让它在不发送任何数据的情况下结束,然后使用isEmpty操作符判、断其是否为空,如代码所示。
java
@Test
public void testIsEmpty() {
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onComplete();
}
}).isEmpty().subscribe(e -> System.out.println("isEMpty:" + e));
}
isEMpty:true
订阅后输出了true,说明我们刚刚创建的Observable确实是一个空的Observable。
7.5、defaultIfEmpty
defaultIfEmpty操作符会判断源Observable是否发送了数据,如果源Observable发送了数据,则正常发送这些数据;否则发送一个默认的数据,其示意图如图所示。

下面创建一个空的Observable和一个非空的Observable,.分别用defaultIfEmpty操作符处理,如果为空则发送出数据10,如代码所示。
java
@Test
public void testDefaultEmpty() {
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onComplete();
}
}).defaultIfEmpty(10).subscribe(e -> System.out.println("empty:" + e));
Observable.just(1).defaultIfEmpty(10)
.subscribe(e -> System.out.println("notEmpty:" + e));
}
empty:10
notEmpty:1
订阅后输出结果如下。因为第一个Observable为空,所以defaultIfEmpty将数据l0发送了出来;而第二个Observable发送了数据1,不为空,所以正常发送数据。
7.6、switchIfEmpty
如果源Observable为空,切换到另一个Observable
java
@Test
public void testSwitchIfEmpty() {
Observable.empty()
.switchIfEmpty(Observable.just(1, 2, 3))
.subscribe(System.out::println);
}
1
2
3
7.7、sequenceEqual
sequenceEqual操作符用来判断两个Observable发送的数据序列是否相同(发送的数据相同、数据的序列相同、结束的状态相同),如果全部相同则返回true,否则返回false,其示意图如图所示。

下面使用sequenceEqual操作符分别来比较两个发送数据相同的Observable和两个发送数据不同的Observable,如代码所示,然后订阅查看结果。
java
@Test
public void testSequenceEqual() {
Observable.sequenceEqual(Observable.just(1, 2, 3),
Observable.just(1, 2, 3))
.subscribe(e -> System.out.println("equal:" + e));
Observable.sequenceEqual(Observable.just(1, 2, 3),
Observable.just(1, 2))
.subscribe(e -> System.out.println("notEqual:" + e));
}
equal:true
notEqual:false
订阅后的输出结果如下。可以看到sequenceEqual操作符将两组Observable的比较结果正确地输出了出来。
7.8、skipUntil和skipWhile
这两个操作符都是根据条件来跳过一些数据,不同之处在于skipUntil是根据一个标志Observable来判断的,当这个标志Observable没有发送数据的时候,所有源Observable发送的数据都会被跳过;当标志Observable发送了一个数据后,则开始正常地发送数据。其示意图如图所示。

而skip While则是根据一个函数来判断是否跳过数据,如果函数返回值为true,则一直跳过源Observable发送的数据;如果函数返回false,则开始正常发送数据,其示意图如图所示。

下面使用interval操作符创建两个操作符,每隔一秒发送一个数据。然后分别用skipUntil和skip While来跳过一些数据,如代码所示。
java
@Test
public void testSkip() {
Observable.interval(1, TimeUnit.SECONDS)
.skipUntil(Observable.timer(3, TimeUnit.SECONDS))
.subscribe(e -> System.out.println("skipUntil:" + e));
Observable.interval(1, TimeUnit.SECONDS)
.skipWhile(l -> l < 5)
.subscribe(e -> System.out.println("skipWhile:" + e));
try {
Thread.sleep(9000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
skipUntil:3
skipUntil:4
skipUntil:5
skipWhile:5
skipUntil:6
skipWhile:6
skipUntil:7
skipWhile:7
skipWhile:8
skipUntil:8
订阅后的输出结果如下。对于第一个Observable,我们采用了timer操作符来创建标志Observable,所以跳过了源Observable的前两个数据;对于第二个Observable,我们的条件是小于5的数据都跳过,所以最终的数据是过了5秒后从5开始发送出来。
7.9、takeUntil和takeWhile
takeUntil和takeWhile操作符分别和skipUnitl及skipWhile操作符是完全相反的功能。takeUntil也是使用一个标志Observable是否发送数据来进行判断:当标志Observable没有发送数据时,正常发送数据,而一旦标志Observable发送过了数据,则后面的数据都会被丢弃,其示意图如图所示。

take While则是根据一个函数来判断是否发送数据,当函数返回值为true的时候正常发送数据;当函数返回值为flse的时候丢弃其后面所有的数据。其示意图如图所示。

下面使用interval操作符创建两个Observable,分别使用takeUntil和takeWhile操作符来取数据,如代码所示。
java
@Test
public void testTake() {
Observable.interval(1, TimeUnit.SECONDS)
.takeUntil(Observable.timer(3, TimeUnit.SECONDS))
.subscribe(e -> System.out.println("takeUntil:" + e));
Observable.interval(1, TimeUnit.SECONDS)
.takeWhile(l -> l < 5)
.subscribe(e -> System.out.println("takeWhile:" + e));
try {
Thread.sleep(9000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
takeUntil:0
takeWhile:0
takeUntil:1
takeWhile:1
takeUntil:2
takeWhile:2
takeWhile:3
takeWhile:4
订阅后输出的结果如下。可以看到第一个Observable因为使用timer创建标志Observable,所以只取了前两个数;第二个Observable只能把小于5的数据发送出来,大于5的数据都被丢弃了。
7.10、条件操作符使用场景
7.10.1、数据验证
java
// 验证所有数据都满足条件
dataSource
.all(this::isValid)
.subscribe(valid -> {
if (valid) {
processData();
} else {
showError("数据不合法");
}
});
7.10.2、回退机制
java
// 主数据源失败时使用备用源
getDataFromApi()
.switchIfEmpty(getDataFromCache())
.defaultIfEmpty("No data available")
.subscribe(this::displayData);
7.10.3、竞态条件处理
java
// 多个数据源竞速,使用最快的
Observable<Data> api1 = getFromApi1();
Observable<Data> api2 = getFromApi2();
Observable<Data> cache = getFromCache();
Observable.ambArray(api1, api2, cache)
.firstElement()
.subscribe(this::processData);
7.10.4、条件过滤
java
// 根据条件动态过滤
dataStream
.skipUntil(userActionObservable) // 等待用户操作
.takeWhile(data -> !data.isComplete()) // 直到完成
.subscribe(this::processData);
八、聚合操作符
8.1、concat
concat操作符将多个Observable结合成一个Observable并发送数据,并且严格按照先后顺序发送数据,即前一个Observable的数据没有发送完时,后面的Observable是不能发送数据的。其示意图如图所示。

有两个操作符与concat操作符很类似,它们分别是:
- startWith:仅仅是在前面插上一个Observable或者一些数据,并且先发送插入的内容。
- merge:其发送的数据是无序的,也就是说被组合的多个Observable是可以自由发送数据的,而不用管其他Observable的状态。
代码将使用just操作符创建三个Observable,发送不同的数据,然后使用concat操作符将其组合起来并进行订阅。
java
@Test
public void testConcat() {
Observable<Integer> o1 = Observable.just(1, 2, 3);
Observable<Integer> o2 = Observable.just(4, 5, 6);
Observable<Integer> o3 = Observable.just(7, 8, 9);
Observable.concat(o1, o2, o3)
.subscribe(e -> System.out.println("concat:" + e));
}
concat:1
concat:2
concat:3
concat:4
concat:5
concat:6
concat:7
concat:8
concat:9
订阅后的结果如下。可以看到,所有的Observable严格按照组合时的顺序来发送数据,只有前一个Observable发送完所有的数据时,后一个Observable才开始发送数据。
8.2、count
count操作符用来统计源Observable发送了多少个数据,最后将数目发送出来。如果源Observable发送错误,则会将错误直接报出来。在源Observable停止发送前,count是不会发送统计数据的,其示意图如图所示。

java
@Test
public void testCount() {
Observable.just(1, 2, 3)
.count()
.subscribe(e -> System.out.println("count:" + e ));
}
count:3
8.3、reduce
reduce操作符应用一个函数接收Observable发送的数据和函数的计算结果,作为下次计算的参数,并输出最后的结果。reduce与我们前面了解过的scan操作符很类似,只是scan会输出每次计算的结果,而reduce只输出最后的结果。reduce的示意图如图所示。

我们首先创建一个包含10个2的list,然后使用from操作符以这个1ist为基础创建一个发送l0个2的Observable,并使用reduce操作符来计算最后的结果,如代码所示。
java
@Test
public void testReduce() {
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(2);
}
Observable.fromIterable(list)
.reduce((x, y) -> x + y)
.subscribe(e -> System.out.println("reduce:" + e));
}
reduce:20
java
// 基本用法
Observable.range(1, 5)
.reduce(0, (sum, item) -> sum + item)
.subscribe(result -> System.out.println("Sum: " + result));
// 输出: Sum: 15
// 无初始值
Observable.range(1, 5)
.reduce((a, b) -> a + b)
.subscribe(result -> System.out.println("Sum: " + result.get()));
// 输出: Sum: 15
8.4、collect
collect操作符类似于reduce,但是二者目的不同。collect操作符用来将源Observable发送的数据收集到一个数据结构里面,最后将这个数据结构整个发送出来。collect操作符需要使用
两个函数作为参数:
- 第一个函数会产生收集数据结构的函数。
- 第二个函数会将上面函数产生的数据结构和源Observable发送的数据作为参数,且这个函数会将源Observable发送的数据存入到这个数据结构中。
collect操作符的示意图如图所示。

java
@Test
public void tstCollect() {
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(2);
}
Observable.fromIterable(list)
.collect(() -> new ArrayList(), new BiConsumer<ArrayList, Integer>() {
@Override
public void accept(ArrayList arrayList, Integer integer) throws Exception {
arrayList.add(integer);
}
})
.subscribe(e -> System.out.println("collect:" + e));
}
collect:[2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
8.5、容器聚合操作符
8.5.1、toList
- 功能:收集所有数据到List
- 返回:Single<List>
java
Observable.just("Apple", "Banana", "Cherry")
.toList()
.subscribe(list -> System.out.println("List: " + list));
// 输出: List: [Apple, Banana, Cherry]
// 指定容量
Observable.range(1, 1000)
.toList(100) // 初始容量
.subscribe(list -> System.out.println("Size: " + list.size()));
8.5.2、toSortedList
java
Observable.just(5, 3, 9, 1, 7)
.toSortedList()
.subscribe(sorted -> System.out.println("Sorted: " + sorted));
// 输出: Sorted: [1, 3, 5, 7, 9]
// 自定义排序
Observable.just("Banana", "Apple", "Cherry")
.toSortedList(Comparator.reverseOrder())
.subscribe(sorted -> System.out.println("Reverse sorted: " + sorted));
// 输出: Reverse sorted: [Cherry, Banana, Apple]
8.5.3、toMap
java
Observable<User> users = Observable.just(
new User(1, "Alice"),
new User(2, "Bob"),
new User(3, "Charlie")
);
// 简单toMap
users.toMap(User::getId)
.subscribe(map -> System.out.println("User Map: " + map));
// 输出: {1=User{1, Alice}, 2=User{2, Bob}, 3=User{3, Charlie}}
// 指定value转换
users.toMap(
User::getId,
User::getName
)
.subscribe(map -> System.out.println("ID-Name Map: " + map));
// 输出: {1=Alice, 2=Bob, 3=Charlie}
8.5.4、toMultimap
java
Observable<Student> students = Observable.just(
new Student("Math", "Alice"),
new Student("Math", "Bob"),
new Student("Science", "Charlie"),
new Student("Math", "David")
);
// 创建Map<String, List<Student>>
students.toMultimap(
Student::getSubject,
Student::getName
)
.subscribe(map -> {
System.out.println("Students by subject:");
map.forEach((subject, names) ->
System.out.println(" " + subject + ": " + names));
});
// 输出:
// Students by subject:
// Math: [Alice, Bob, David]
// Science: [Charlie]
九、Connectable Observable相关操作符
首先我们来回顾一下前面所学的Observable,它们有一个共性,那就是只有当订阅者来订阅时才会开始发送数据,否则什么也不会发生,这就是懒加载。那什么是Connectable Observable呢?它是一种特殊的Observable,并不是在订阅者订阅时才发送数据,而是只要对其应用connect操作符就开始发送数据。所以如果在对其应用connect操作符之前进行订阅的话,并不能让Connectable Observable发送数据。
9.1、publish和connect
publish操作符就是用来将一个普通的Observable转化为一个Connectable Observable的。需要注意的是,如果发送数据已经开始了再进行订阅的话,就只能接收以后发送的数据。其示意图如图所示。

connect操作符就是用来触发Connectable Observable发送数据的。应用connect操作符后会返回一个Subscription对象,通过这个Subscription对象,我们可以调用其unsubscribe方法来中止数据的发送。另外,如果还没有订阅者订阅就应用connect操作符,也是可以使其开始发送数据的。
下面使用interval操作符创建一个Observable,.它每隔一秒发送一个数据,然后使用publish操作符将其转化为一个Connectable Observable。然后创建两个订阅者,让订阅者1订阅到前面创建的Connectable Observable,并且监控发送的数据,当数据为3时把订阅者2也订阅上,如代码所示。
java
@Test
public void testPublish() {
ConnectableObservable<Long> obser = Observable.interval(1, TimeUnit.SECONDS)
.observeOn(Schedulers.newThread())
.publish();
obser.subscribe(e -> {
System.out.println("consumer1:" + e);
if (e == 3)
obser.subscribe(l -> System.out.println("consumer2:" + l));
});
obser.connect();
try {
Thread.sleep(6000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
consumer1:0
consumer1:1
consumer1:2
consumer1:3
consumer1:4
consumer2:4
consumer1:5
consumer2:5
9.2、refCount
refCount操作符能够将一个Connectable Observable对象再重新转化为一个普通的Observable对象,这时候如果有订阅者进行订阅将会触发数据的发送,其示意图如图所示。

如代码所示,我们首先如上文一样使用publish创建一个Connectable Observable对象,然后再使用ref℃ount将其转化为一个普通的Observable对象,最后对其进行订阅。
java
@Test
public void testRefCount() {
Observable.interval(1, TimeUnit.SECONDS)
.observeOn(Schedulers.newThread())
.publish()
.refCount()
.subscribe(e -> System.out.println("refCount:" + e));
try {
Thread.sleep(6000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
refCount:0
refCount:1
refCount:2
refCount:3
refCount:4
refCount:5