前面两篇文章中我们介绍了RxJava的一些基本概念和RxJava最简单的用法。从这一章开始,我们开始聊聊RxJava中的操作符Operators,后面我将用三章的篇幅来分别介绍:

  1. 转换类操作符
  2. 过滤类操作符
  3. 组合类操作符

这一章我们主要讲讲转换类操作符。所有这些Operators都作用于一个可观测序列,然后变换它发射的值,最后用一种新的形式返回它们。概念实在是不好理解,下面我们结合实际的例子一一介绍。

Map

map(Func1)函数接受一个Func1类型的参数(就像这样map(Func1<? super T, ? extends R> func)),然后吧这个Func1应用到每一个由Observable发射的值上,将发射的值转换为我们期望的值。这种狗屁定义我相信你也听不懂,我们来看一下官方给出的原理图:

假设我们需要将一组数字转换成字符串,我们可以通过map这样实现:

Observable.just(1, 2, 3, 4, 5)
.map(new Func1<Integer, String>() { @Override
public String call(Integer i) {
return "This is " + i;
}
}).subscribe(new Action1<String>() {
@Override
public void call(String s) {
System.out.println(s);
}
});

Func1构造函数中的两个参数分别是Observable发射值当前的类型和map转换后的类型,上面这个例子中发射值当前的类型是Integer,转换后的类型是String。

FlatMap

flatMap(Func1)函数同样也是做转换的,但是作用却不一样。flatMap不太好理解,我们直接看例子(我们公司是个房产平台,那我就拿房子举例):假设我们有一组小区数据Community[] communites,现在我们要输出每个小区的名字;我们可以这样实现:

Observable.from(communities)
.map(new Func1<Community, String>() { @Override
public String call(Community community) {
return community.name;
}
})
.subscribe(new Action1<String>() {
@Override
public void call(String name) {
System.out.println("Community name : " + name);
}
});

现在我们需求有变化,需要打印出每个小区下面每一套房子的价格。于是我可以这样实现:

Community[] communities = {};
Observable.from(communities)
.subscribe(new Action1<Community>() {
@Override
public void call(Community community) {
for (House house : community.houses) {
System.out.println("House price : " + house.price);
}
}
});

如果我不想在Subscriber中使用for循环,而是希望Subscriber中直接传入单个的House对象呢?用map()显然是不行的,因为map()是一对一的转化,而我现在的要求是一对多的转化。那么我们可以使用flatMap()把一个Community转化成多个House。

Observable.from(communities)
.flatMap(new Func1<Community, Observable<House>>() {
@Override
public Observable<House> call(Community community) {
return Observable.from(community.houses);
}
})
.subscribe(new Action1<House>() {
@Override
public void call(House house) {
System.out.println("House price : " + house.price);
}
});

从前面的例子中我们发现,flatMap()和map()都是把传入的参数转化之后返回另一个对象。但和map()不同的是,flatMap()中返回的是Observable对象,并且这个Observable对象并不是被直接发送到 Subscriber的回调方法中。

flatMap(Func1)的原理是这样的:

  1. 将传入的事件对象装换成一个Observable对象;
  2. 这是不会直接发送这个Observable, 而是将这个Observable激活让它自己开始发送事件;
  3. 每一个创建出来的Observable发送的事件,都被汇入同一个Observable,这个Observable负责将这些事件统一交给Subscriber的回调方法。

这三个步骤,把事件拆成了两级,通过一组新创建的Observable将初始的对象『铺平』之后通过统一路径分发了下去。而这个『铺平』就是flatMap()所谓的flat。

最后我们来看看flatMap的原理图:

ConcatMap

concatMap(Func1)解决了flatMap()的交叉问题,它能够把发射的值连续在一起,就像这样:

flatMapIterable

flatMapIterable(Func1)flatMap()几乎是一样的,不同的是flatMapIterable()它转化的多个Observable是使用Iterable作为源数据的。

Observable.from(communities)
.flatMapIterable(new Func1<Community, Iterable<House>>() {
@Override
public Iterable<House> call(Community community) {
return community.houses;
}
})
.subscribe(new Action1<House>() { @Override
public void call(House house) { }
});

SwitchMap

switchMap(Func1)flatMap(Func1)很像,除了一点:每当源Observable发射一个新的数据项(Observable)时,它将取消订阅并停止监视之前那个数据项产生的Observable,并开始监视当前发射的这一个。

Scan

scan(Func2)对一个序列的数据应用一个函数,并将这个函数的结果发射出去作为下个数据应用合格函数时的第一个参数使用。

我们来看个简单的例子:

Observable.just(1, 2, 3, 4, 5)
.scan(new Func2<Integer, Integer, Integer>() {
@Override
public Integer call(Integer integer, Integer integer2) {
return integer + integer2;
}
}).subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
System.out.print(integer+“ ”);
}
});

输出结果为:

1 3 6 10 15

GroupBy

groupBy(Func1)将原始Observable发射的数据按照key来拆分成一些小的Observable,然后这些小Observable分别发射其所包含的的数据,和SQL中的groupBy类似。实际使用中,我们需要提供一个生成key的规则(也就是Func1中的call方法),所有key相同的数据会包含在同一个小的Observable中。另外我们还可以提供一个函数来对这些数据进行转化,有点类似于集成了flatMap。

单纯的文字描述和图片解释可能难以理解,我们来看个例子:假设我现在有一组房源List<House> houses,每套房子都属于某一个小区,现在我们需要根据小区名来对房源进行分类,然后依次将房源信息输出。

List<House> houses = new ArrayList<>();
houses.add(new House("中粮·海景壹号", "中粮海景壹号新出大平层!总价4500W起"));
houses.add(new House("竹园新村", "满五唯一,黄金地段"));
houses.add(new House("中粮·海景壹号", "毗邻汤臣一品"));
houses.add(new House("竹园新村", "顶层户型,两室一厅"));
houses.add(new House("中粮·海景壹号", "南北通透,豪华五房"));
Observable<GroupedObservable<String, House>> groupByCommunityNameObservable = Observable.from(houses)
.groupBy(new Func1<House, String>() { @Override
public String call(House house) {
return house.communityName;
}
});

通过上面的代码我们创建了一个新的Observable:groupByCommunityNameObservable,它将会发送一个带有GroupedObservable的序列(也就是指发送的数据项的类型为GroupedObservable)。GroupedObservable是一个特殊的Observable,它基于一个分组的key,在这个例子中的key就是小区名。现在我们需要将分类后的房源依次输出:

Observable.concat(groupByCommunityNameObservable)
.subscribe(new Action1<House>() {
@Override
public void call(House house) {
System.out.println("小区:"+house.communityName+"; 房源描述:"+house.desc);
}
});

执行结果:

小区:中粮·海景壹号; 房源描述:中粮海景壹号新出大平层!总价4500W起
小区:中粮·海景壹号; 房源描述:毗邻汤臣一品
小区:中粮·海景壹号; 房源描述:南北通透,豪华五房
小区:竹园新村; 房源描述:满五唯一,黄金地段
小区:竹园新村; 房源描述:顶层户型,两室一厅

转换类的操作符就先介绍到这,后续还会继续介绍组合、过滤类的操作符及源码分析,敬请期待!

如果你喜欢我的文章,就关注下我的知乎专栏或者在 GitHub 上添个 Star 吧!

RxJava系列3(转换操作符)的更多相关文章

  1. RxJava系列5(组合操作符)

    RxJava系列1(简介) RxJava系列2(基本概念及使用介绍) RxJava系列3(转换操作符) RxJava系列4(过滤操作符) RxJava系列5(组合操作符) RxJava系列6(从微观角 ...

  2. RxJava系列4(过滤操作符)

    RxJava系列1(简介) RxJava系列2(基本概念及使用介绍) RxJava系列3(转换操作符) RxJava系列4(过滤操作符) RxJava系列5(组合操作符) RxJava系列6(从微观角 ...

  3. RxJava系列番外篇:一个RxJava解决复杂业务逻辑的案例

    之前写过一系列RxJava的文章,也承诺过会尽快有RxJava2的介绍.无奈实际项目中还未真正的使用RxJava2,不敢妄动笔墨.所以这次还是给大家分享一个使用RxJava1解决问题的案例,希望对大家 ...

  4. RxJava系列7(最佳实践)

    RxJava系列1(简介) RxJava系列2(基本概念及使用介绍) RxJava系列3(转换操作符) RxJava系列4(过滤操作符) RxJava系列5(组合操作符) RxJava系列6(从微观角 ...

  5. RxJava系列6(从微观角度解读RxJava源码)

    RxJava系列1(简介) RxJava系列2(基本概念及使用介绍) RxJava系列3(转换操作符) RxJava系列4(过滤操作符) RxJava系列5(组合操作符) RxJava系列6(从微观角 ...

  6. RxJava系列2(基本概念及使用介绍)

    RxJava系列1(简介) RxJava系列2(基本概念及使用介绍) RxJava系列3(转换操作符) RxJava系列4(过滤操作符) RxJava系列5(组合操作符) RxJava系列6(从微观角 ...

  7. RxJava系列1(简介)

    RxJava系列1(简介) RxJava系列2(基本概念及使用介绍) RxJava系列3(转换操作符) RxJava系列4(过滤操作符) RxJava系列5(组合操作符) RxJava系列6(从微观角 ...

  8. RxJava系列之二 变换类操作符具体解释1

    1.回想 上一篇文章我们主要介绍了RxJava , RxJava 的Observables和 RxJava的just操作符.以及RxJava一些经常使用的操作. 没看过的抓紧点我去看吧. 事实上RxJ ...

  9. RxJava【创建】操作符 create just from defer timer interval MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

随机推荐

  1. 设计模式——桥接模式(C++实现)

    [root@ ~/learn_code/design_pattern/18_bridge]$ cat Abstraction.h #ifndef _ABSTRACTION_H_ #define _AB ...

  2. Mycat 分片规则详解--应用指定分片

    实现方式:根据字符串的子串(必须是数字)计算分区号(由调用方传递参数,显示指定分区号),例如,id=05-12232323,其中 id 是从 startIndex = 0,size=2,即截取的子串是 ...

  3. 常用数据库连接池 (DBCP、c3p0、Druid) 配置说明

    1. 引言 1.1 定义 数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库 ...

  4. dubbo + dubbo-admin 入门级demo

    整个示例中,除了dubbo的服务提供者和消费者是在windows里跑着,其他的所有程序都是在centos中. 1.准备环境 windows中jdk和java ide下载. 在centos中,创建软件安 ...

  5. 分析 webpack 打包后的代码

    写在前面的:使用的 webpack 版本 3.0.0 一.开始 1. 准备 当前只创建一个文件 index.js,该文件作为入口文件,代码如下. console.log('hello, world') ...

  6. 每天学习点--------第六天(2017-10-10) 摘要: mysql和Oracle的区别

    1.自动增长数据类型的处理 Mysql有自动增长的数据类型,插入记录时不用操作此字段,会自动获取数据值.Oracle没有自动增长的数据类型,需要建立一个自动增长的序列号,插入记录时要把序列号的下一个值 ...

  7. (转)[置顶] Android APK反编译就这么简单 详解(附图) .

    在学习Android开发的过程你,你往往会去借鉴别人的应用是怎么开发的,那些漂亮的动画和精致的布局可能会让你爱不释手,作为一个开发者,你可能会很想知道这些效果界面是怎么去实现的,这时,你便可以对改应用 ...

  8. django 连接mysql

    环境 Linux 修改工程目录下的settings.py 文件 #!!!!!!!!切勿出现中文 即便//注释也不行 DATABASES = { 'default': { 'ENGINE': 'djan ...

  9. SQL之Left Join 关联条件的探讨

    在测试工作中,有时需要测试数据库数据经过sql计算后的结果是否满足某一功能查询得到的返回值. 针对某些需要功能需要联查多张表,此时 关联 的作用就异常重要了,而针对多表关联,其中 关联条件的重要性不言 ...

  10. vue-router 组件实例被复用问题

    最近在开发过程中遇到如下问题: 当前路由是这样的 http://127.0.0.1:3010/order?keywords=22 只改变keywords的值,路由不跳转 http://127.0.0. ...