我正在尝试了解RxJava
的细节。
直觉上,我希望first()
和take(1)
是平等的,并且做同样的事情。但是,通过挖掘源代码first()
被定义为take(1).single()
。
这里的single()
有什么用?take(1)
不是已经保证输出单个项目了吗?
不同之处在于,take(1)
将从上游中继 0..1 个项目,而first
将中继第一个元素或在上游为空时发出错误 (NoSuchElementException)。他们都没有阻止。
确实first == take(1).single()
take(1)
将上游项目的数量限制为 1 并且single()
确保上游不为空。
本示例仅打印"完成"
Observable.empty().take(1)
.subscribe(System.out::println,
Throwable::printStackTrace,
() -> System.out.println("Done"));
本示例打印"1",后跟"完成":
Observable.just(1).take(1)
.subscribe(System.out::println,
Throwable::printStackTrace,
() -> System.out.println("Done"));
此示例还打印"1",后跟"完成":
Observable.just(1, 2, 3).take(1)
.subscribe(System.out::println,
Throwable::printStackTrace,
() -> System.out.println("Done"));
此示例失败,并显示NoSuchElementException
Observable.empty().first()
.subscribe(System.out::println,
Throwable::printStackTrace,
() -> System.out.println("Done"));
此示例再次打印"1",后跟"完成":
Observable.just(1).first()
.subscribe(System.out::println,
Throwable::printStackTrace,
() -> System.out.println("Done"));
此示例再次打印"1",后跟"完成":
Observable.just(1, 2, 3).first()
.subscribe(System.out::println,
Throwable::printStackTrace,
() -> System.out.println("Done"));
此示例打印 NoSuchElementException
的堆栈跟踪,因为源包含的元素太少:
Observable.empty().single()
.subscribe(System.out::println,
Throwable::printStackTrace,
() -> System.out.println("Done"));
此示例打印IllegalArgumentException
的堆栈跟踪,因为源包含太多元素:
Observable.just(1, 2, 3).single()
.subscribe(System.out::println,
Throwable::printStackTrace,
() -> System.out.println("Done"));
实现的差异是由语义的差异驱动的。 在对语言及其内部工作原理一无所知的情况下,请考虑这两种方法的含义。
first()
意味着它将只返回一个元素。 集合中的第一个元素。
take(x)
意味着它将返回元素集合。 集合中的第一个x
元素。
语义需要不同的名称。 不同的返回值需要不同的技术实现。
也有可能(同样,无需查看引擎盖以获得技术答案),这两种实现可能以非常不同的方式处理错误条件。 我个人希望first()
为空集合抛出异常,因为没有第一个元素。 但是,如果初始集合的元素少于 x
,我可能会合理地期望take(x)
返回小于 x
大小的集合,而不会出错。 这可能会导致每当给定空集合时返回空集合而不会出错。
此外,作为一个技术问题,像 take(x)
这样的东西更有可能不会立即迭代基础集合,而是推迟到某些东西迭代其结果。 但是,first()
需要迭代并具体化基础集合的第一个元素。