【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
Transformation of sequences
在本章中,我们将看到改变数据格式的方法。在现实世界中,observable 可以是任何类型的。数据的格式已经是我们想要的格式,这是不常见的。更有可能的情况是,这些值需要扩展、裁剪、评估或简单地替换为其他值。
这将由 oprations 的三个基本类别来完成。map 和 flatMap 是第三类的基本方法。在文献中,您常常会发现它们被称为“bind”,至于原因超出了本指南的范围。
Ana(morphism) T --> Observable<T>
Cata(morphism) Observable<T> --> T
Bind Observable<T1> --> Observable<T2>
在最后一章中,为了方便起见,我们介绍了 Subscriber 的一个实现。我们将在本章的示例中继续使用它。
map
转换的基本方法是 map(在类似由SQL启发的系统(如LINQ)中也称为“SELECT”)。它接受一个转换函数,它接受一个项并返回任何类型的新项。返回的可观测值由转换函数返回的值组成。
在第一个例子中,我们将取一个整数序列,并将它们增加3。
输出:
这就是 map 做的事情,例如通过使用 Observable.range(3,4)。在下面,我们将做一些更实际的事情。生产者将像许多 UI 通常所做的那样,以字符串的形式发出数值,然后使用map将它们转换为更可处理的整数格式。
输出:
这种转换非常简单,我们也可以在订阅方进行,但这将是一种糟糕的责任划分。在开发生产者方面时,您希望以尽可能简洁和最方便的方式呈现事物。你不会转储原始数据,让消费者自己去发现。在我们的示例中,既然我们说API生成整数,它就应该这样做。Transfomation操作符允许我们将初始序列转换为我们想要公开的API。
cast and ofType
cast 是将 item 转换为另一种类型的简写。如果您有一个 Observable<Object>,您知道它只会发出T类型的值,那么在lambda函数中强制转换可观察到的值比进行强制转换要简单得多。
输出:
如果不能将所有项强制转换为指定类型,则强制转换方法将失败。
输出:
如果您希望忽略此类情况,则可以使用ofType方法。这将过滤无法强制转换的项,然后将序列转换为所需的类型。
输出:
timestamp and timeInterval
timestamp 和 timeInterval 方法使我们能够用有关序列的异步性质的信息来丰富我们的值。timestamp将值转换 Timestamped<T> 类型,该类型包含原始值,以及发出事件时的时间戳
public final Observable<Timestamped<T>> timestamp()
这里是一个例子:
输出:
时间戳使我们可以看到这些项大约是隔100毫秒发出的(Java对此几乎没有提供保证)。
如果我们更感兴趣的是自上一项之后已经过去了多少时间,而不是发出项目的绝对时刻,我们可以使用timeInterval方法
public final Observable<TimeInterval<T>> timeInterval()
按照与前面相同的顺序使用timeInterval:
输出:
timestamp 和TimeInterval捕获的信息对于日志记录和调试非常有用。它是Rx获取序列异步性信息的一种方法。
materialize and dematerialize
materialize 对于日志记录也很有用,materialize 将序列转换为它的元数据表示形式。
通知类型可以表示任何事件,即值的释放、错误或完成。注意,在上面的图中,“onCompleted”的发射并不意味着序列的结束,因为序列实际上是随后结束的。下面是一个例子
输出:
Notification type包含用于确定事件类型以及携带值或Throwable(如果有的话)的方法。
dematerialize 将逆转 materialize 的效果,使 materialized observable 返回到它的正常形式。
flatMap
map取一个值,然后返回另一个值,替换顺序为一对一的项。flatMap 将用任意数量的项替换项目,包括零项或无穷项。flatMap 的转换方法从源 observable 中获取值,并对每个值返回一个新的 observable,发射新值。
由 flatMap 返回的可观测值将发出由转换函数产生的所有可观测值发出的所有值。来自同一可观测值的值将是有序的,但它们可能与来自其他可观测值的值交织在一起。
让我们从一个简单的例子开始,其中 FlapMap 应用于一个具有单个值的 observable ,将发出一个单独的值。FlapMap将把它转换为一个即0到2之间的范围的 observable。这个可观测值是在最终的可观测值中发射出来的。
输出:
当 flatMap 应用于多个值的 observable ,每个值将产生一个新的 observable , values 将发出1、2和3。得到的 observable 将分别发出值[0]、[0,1]和[0,1,2]。这些值将被平铺在一起,形成一个可观察到的值:由FlapMap返回的值。
输出:
与 map 非常相似,platMap 的输入和输出类型可以自由地有所不同。在下一个示例中,我们将把整数转换为字符。
输出:
虽然每个值都必须产生一个可观察的值,但是没有什么可以阻止这个可观察到的值是空的。我们可以用它来过滤序列,同时对其进行转换。
输出:
此示例将导致打印整个字母表而不会出错,即使初始范围超过字母表的范围。
到目前为止,在我们的flatMap 示例中,值按顺序排列:首先是来自第一个可观测值的所有值,然后是来自第二个可观测值的所有值。虽然这似乎很直观,特别是在来自同步环境时,但需要注意的是,情况并不总是如此。FlapMap返回的可观察值一旦可用就会发出值。在我们的例子中,所有的观测值都是同步准备好的。为了证明这一点,我们使用区间方法构造异步观测值。
输出:
我们从值100和150开始,将它们用作在 FlapMap 中创建的异步观测值的间隔期间。因为间隔发出数字1,2,3...在这两种情况下,为了更好地区分这两个可观察到的值,我们用每个可观察到的操作所依据的间隔时间替换这些值。
我们可以看到,这两个观测值交织在一起。
concatMap
尽管在函数式编程中,FlapMap与一个非常常见的操作符同名,但我们发现它的行为并不完全像函数式程序员所期望的那样。有一个运算符不会交错序列,名为contatMap,因为它与我们稍后将看到的conat运算符相关。
输出:
我们可以在输出中看到这两个序列是分开的。请注意,contatMap操作符只适用于终止序列:在当前序列终止之前,它不能移动到下一个序列。由于这个原因,我们不得不用Take来限制区间的无穷序列。
flatMapIterable
flatMap 和 concatMap 将由它们的选择器函数生成的一系列可观察到的东西平铺成一个可观察的东西。我们还可以使用FlapMapIterable来平抑一个迭代序列。这类似于 flatMap ,只有我们的选择器函数创建迭代。
如果取代 Observable.range ,我们可能写下面的迭代器
输出:
正如预期的那样,我们创建的三个迭代在一个可观察的序列中被压平(flattened)。
作为一名Rx开发人员,建议您将数据表示为可观察序列,并避免将可观测序列与迭代序列相混合。但是,当您的数据已经采用集合的格式时(例如,因为标准Java操作会这样返回它们),只使用它们而不首先转换它们可能会更简单或更快。FlapMapIterable还消除了选择交错与否的需要:FlapMapIterable不会交错,就像您期望从同步FlapMap中得到的一样。
还有第二个重载,它允许您将可迭代中的每个值与生成可迭代的值组合在一起。
输出:
在这里,我们将迭代范围中的每个值乘以播种范围的值:[1*1]、[1*2、2*2]、[1*3、2*3、3*3]。
Java缺乏对其标准集合进行映射的方法。因此,在种子值消失之前不可能转换可迭代值(这里是i->范围(1,i)中的i)。在这里,我们的可迭代仅仅是一个列表,所以我们可以在返回它之前修改它。但是,如果我们的可迭代不是一个集合,我们将不得不自己实现一个迭代映射,或者手动将修改后的值收集到一个新的集合中并返回它。这个重载的FlapMapIterable使我们不必将这种丑陋插入到管道的中间。
懒惰的概念在Java中并不常见,因此您可能会对哪些类型的可迭代不是集合感到困惑。为了举例说明,请考虑下面的可迭代性,它懒散地生成一个范围。它允许我们通过计算上一个值的下一个值来迭代一个范围。这样,我们节省了存储整个范围的内存。
输出:
原文链接:
https://github.com/Froussios/Intro-To-RxJava/blob/master/Part%202%20-%20Sequence%20Basics/5.%20Transformation%20of%20sequences.md
来源:oschina
链接:https://my.oschina.net/u/2277632/blog/1787369