转换类操作符(map flatMap concatMap flatMapIterable switchMap scan groupBy…);

Map

理解: map()函数接受一个Func1类型的参数(就像这样map(Func1<? super T, ? extends R> func)),然后把这个Func1应用到每一个由Observable发射的值上,将发射的值转换为我们期望的值。

1
2
3
4
5
6
7
8
9
10
11
12
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);
}
});

flatMap

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

  1. 将传入的事件对象装换成一个Observable对象;
  2. 这是不会直接发送这个Observable, 而是将这个Observable激活让它自己开始发送事件;
  3. 每一个创建出来的Observable发送的事件,都被汇入同一个Observable,这个Observable负责将这些事件统一交给Subscriber的回调方法。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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);
    }
    });
    Observable.just("images/logo.png") // 输入类型 String
    .map(new Func1<String, Bitmap>() {
    @Override
    public Bitmap call(String filePath) { // 参数类型 String
    return getBitmapFromPath(filePath); // 返回类型 Bitmap
    }
    })
    .subscribe(new Action1<Bitmap>() {
    @Override
    public void call(Bitmap bitmap) { // 参数类型 Bitmap
    showBitmap(bitmap);
    }
    });

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

1
2
3
4
5
6
7
8
9
10
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。

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
}
});

concatMap

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

FlatMapIterable

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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) {
}
});

Scan

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

1
2
3
4
5
6
7
8
9
10
11
12
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+“ ”);
}
});

GroupBy

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
}
});
`