데이터 변환 연산자
map
- 원본 Observable에서 통지하는 데이터를 원하는 값으로 변환 후 통지한다.
- 변환 전, 후 데이터 타입은 달라도 상관없다.
- null을 반환하면 NullpointException이 발생하므로 null이 아닌 데이터 하나를 반드시 반환해야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Observable이 통지한 항목에 함수를 적용하여 통지된 값을 변환시킨다.
*/
public class ObservableMapExample01 {
public static void main(String[] args) {
List<Integer> oddList = Arrays.asList(1, 3, 5, 7);
Observable.fromIterable(oddList)
.map(num -> "1을 더한 결과: " + (num + 1))
.subscribe(data -> Logger.log(LogType.ON_NEXT, data));
}
}
/*
onNext() | main | 23:07:16.377 | 1을 더한 결과: 2
onNext() | main | 23:07:16.380 | 1을 더한 결과: 4
onNext() | main | 23:07:16.380 | 1을 더한 결과: 6
onNext() | main | 23:07:16.381 | 1을 더한 결과: 8
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ObservableMapExample02 {
public static void main(String[] args) {
Observable.just("korea", "america", "canada", "paris", "japan", "china")
.filter(country -> country.length() == 5 )
.map(country -> country.toUpperCase().charAt(0) + country.substring(1))
.subscribe(data -> Logger.log(LogType.ON_NEXT, data));
}
}
/*
onNext() | main | 23:08:45.368 | Korea
onNext() | main | 23:08:45.371 | Paris
onNext() | main | 23:08:45.371 | Japan
onNext() | main | 23:08:45.371 | China
*/
flapMap 첫번쨰 유형
- 원본 데이터를 원하는 값으로 변환 후 통지하는것은 map과 같다.
- map이 1 대 1 변환인 것과 달리 flatMap은 1 대 다 변환하므로 데이터 한개로 여러 데이터를 통지할 수 있다.
- map은 변환된 데이터를 반환하지만 flatMap은 변환 된 여러개의 데이터를 담고 있는 새로운 Observable을 반환한다.
1
2
3
4
5
6
7
8
9
10
11
12
public class ObservableFlatMapExample01 {
public static void main(String[] args) {
Observable.just("Hello")
.flatMap(hello -> Observable.just("자바", "파이썬", "안드로이드").map(lang -> hello + ", " + lang))
.subscribe(data -> Logger.log(LogType.ON_NEXT, data));
}
}
/*
onNext() | main | 23:31:35.508 | Hello, 자바
onNext() | main | 23:31:35.511 | Hello, 파이썬
onNext() | main | 23:31:35.511 | Hello, 안드로이드
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* flatMap을 이용한 구구단의 2단 출력 예제
*/
public class ObservableFlatMapExample02 {
public static void main(String[] args) {
Observable.range(2, 1)
.flatMap(num -> Observable.range(1, 9)
.map(row -> num + " x " + row + " = " + num * row)
)
.subscribe(data -> Logger.log(LogType.ON_NEXT, data));
}
}
/*
onNext() | main | 23:36:17.510 | 2 x 1 = 2
onNext() | main | 23:36:17.516 | 2 x 2 = 4
onNext() | main | 23:36:17.516 | 2 x 3 = 6
onNext() | main | 23:36:17.517 | 2 x 4 = 8
onNext() | main | 23:36:17.517 | 2 x 5 = 10
onNext() | main | 23:36:17.517 | 2 x 6 = 12
onNext() | main | 23:36:17.518 | 2 x 7 = 14
onNext() | main | 23:36:17.518 | 2 x 8 = 16
onNext() | main | 23:36:17.518 | 2 x 9 = 18
*/
flatMap 두번째 유형
- 원본 데이터와 변환된 데이터를 조합해서 새로운 데이터를 통지한다.
- 즉, observable에 원본 데이터 + 변환된 데이터 = 최종 데이터 를 실어서 반환한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* flatMap을 두번쨰 유형을 이용한 구구단의 2단 출력 예제
*/
public class ObservableFlatMapExample03 {
public static void main(String[] args) {
Observable.range(2, 1)
.flatMap(num -> Observable.range(1, 9),
(sourceData, transformedData) ->
sourceData + " x " + transformedData + " = " + sourceData * transformedData
)
.subscribe(data -> Logger.log(LogType.ON_NEXT, data));
}
}
/*
onNext() | main | 23:53:33.369 | 2 x 1 = 2
onNext() | main | 23:53:33.372 | 2 x 2 = 4
onNext() | main | 23:53:33.373 | 2 x 3 = 6
onNext() | main | 23:53:33.373 | 2 x 4 = 8
onNext() | main | 23:53:33.373 | 2 x 5 = 10
onNext() | main | 23:53:33.373 | 2 x 6 = 12
onNext() | main | 23:53:33.373 | 2 x 7 = 14
onNext() | main | 23:53:33.373 | 2 x 8 = 16
onNext() | main | 23:53:33.373 | 2 x 9 = 18
*/
concatMap
- flatMap과 마찬가지로 받은 데이터를 받은 데이터를 변환하여 새오누 Observable로 반환한다.
- 반환된 새로운 Observable을 하나씩 순서대로 실행하는것이 FlatMap과 다르다.
- 즉, 데이터의 처리 순서는 보장하지만 처리중인 Observable의 처리가 끝나야 다음 Observable이 실행되므로 처리 성능에는 영향을 줄 수 있다.
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* 순서를 보장해주는 concatMap 예제
* 순차적으로 실행되기 때문에 flapMap보다 느리다.
*/
public class ObservableConcatMapExample01 {
public static void main(String[] args) {
TimeUtil.start();
Observable.interval(100L, TimeUnit.MILLISECONDS)
.take(4)
.skip(2)
.concatMap(
num -> Observable.interval(200L, TimeUnit.MILLISECONDS)
.take(10)
.skip(1)
.map(row -> num + " x " + row + " = " + num * row)
).subscribe(
data -> Logger.log(LogType.ON_NEXT, data),
error -> {
},
() -> {
TimeUtil.end();
TimeUtil.takeTime();
}
);
TimeUtil.sleep(5000L);
}
}
/*
onNext() | RxComputationThreadPool-2 | 00:21:09.166 | 2 x 1 = 2
onNext() | RxComputationThreadPool-2 | 00:21:09.332 | 2 x 2 = 4
onNext() | RxComputationThreadPool-2 | 00:21:09.532 | 2 x 3 = 6
onNext() | RxComputationThreadPool-2 | 00:21:09.732 | 2 x 4 = 8
onNext() | RxComputationThreadPool-2 | 00:21:09.932 | 2 x 5 = 10
onNext() | RxComputationThreadPool-2 | 00:21:10.132 | 2 x 6 = 12
onNext() | RxComputationThreadPool-2 | 00:21:10.332 | 2 x 7 = 14
onNext() | RxComputationThreadPool-2 | 00:21:10.533 | 2 x 8 = 16
onNext() | RxComputationThreadPool-2 | 00:21:10.732 | 2 x 9 = 18
onNext() | RxComputationThreadPool-3 | 00:21:11.136 | 3 x 1 = 3
onNext() | RxComputationThreadPool-3 | 00:21:11.336 | 3 x 2 = 6
onNext() | RxComputationThreadPool-3 | 00:21:11.537 | 3 x 3 = 9
onNext() | RxComputationThreadPool-3 | 00:21:11.736 | 3 x 4 = 12
onNext() | RxComputationThreadPool-3 | 00:21:11.937 | 3 x 5 = 15
onNext() | RxComputationThreadPool-3 | 00:21:12.134 | 3 x 6 = 18
onNext() | RxComputationThreadPool-3 | 00:21:12.337 | 3 x 7 = 21
onNext() | RxComputationThreadPool-3 | 00:21:12.537 | 3 x 8 = 24
onNext() | RxComputationThreadPool-3 | 00:21:12.737 | 3 x 9 = 27
# 실행시간: 4468 ms
*/
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* concatMap과 달리 순서를 보장해주지 않는 flatMap의 에제
* 실행 속도가 concatMap 보다 빠르다.
*/
public class ObservableConcatMapExample02 {
public static void main(String[] args) {
TimeUtil.start();
Observable.interval(100L, TimeUnit.MILLISECONDS)
.take(4)
.skip(2)
.flatMap(
num -> Observable.interval(200L, TimeUnit.MILLISECONDS)
.take(10)
.skip(1)
.map(row -> num + " x " + row + " = " + num * row)
).subscribe(
data -> Logger.log(LogType.ON_NEXT, data),
error -> {
},
() -> {
TimeUtil.end();
TimeUtil.takeTime();
}
);
TimeUtil.sleep(3000L);
}
}
/*
onNext() | RxComputationThreadPool-2 | 00:22:57.360 | 2 x 1 = 2
onNext() | RxComputationThreadPool-3 | 00:22:57.418 | 3 x 1 = 3
onNext() | RxComputationThreadPool-2 | 00:22:57.523 | 2 x 2 = 4
onNext() | RxComputationThreadPool-3 | 00:22:57.619 | 3 x 2 = 6
onNext() | RxComputationThreadPool-2 | 00:22:57.721 | 2 x 3 = 6
onNext() | RxComputationThreadPool-3 | 00:22:57.818 | 3 x 3 = 9
onNext() | RxComputationThreadPool-2 | 00:22:57.921 | 2 x 4 = 8
onNext() | RxComputationThreadPool-3 | 00:22:58.018 | 3 x 4 = 12
onNext() | RxComputationThreadPool-2 | 00:22:58.121 | 2 x 5 = 10
onNext() | RxComputationThreadPool-3 | 00:22:58.217 | 3 x 5 = 15
onNext() | RxComputationThreadPool-2 | 00:22:58.321 | 2 x 6 = 12
onNext() | RxComputationThreadPool-3 | 00:22:58.418 | 3 x 6 = 18
onNext() | RxComputationThreadPool-2 | 00:22:58.521 | 2 x 7 = 14
onNext() | RxComputationThreadPool-3 | 00:22:58.618 | 3 x 7 = 21
onNext() | RxComputationThreadPool-2 | 00:22:58.721 | 2 x 8 = 16
onNext() | RxComputationThreadPool-3 | 00:22:58.819 | 3 x 8 = 24
onNext() | RxComputationThreadPool-2 | 00:22:58.920 | 2 x 9 = 18
onNext() | RxComputationThreadPool-3 | 00:22:59.018 | 3 x 9 = 27
# 실행시간: 2798 ms
*/
switchMap
- concatMap과 마찬가지로 받은 데이터를 변환하여 새로운 Observable로 반환한다.
- concatMap과 다른점은 switchMap은 순서를 보장하지만 새로운 데이터가 통지되면 현재 처리중이던 작업을 바로 중단한다.
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* 원본 소스의 처리 속도가 빨라서 현재 처리 중이던 작업을 중단하는 예제
*/
public class ObservableSwitchMapExample01 {
public static void main(String[] args) {
TimeUtil.start();
Observable.interval(100L, TimeUnit.MILLISECONDS)
.take(4)
.skip(2)
.doOnNext(data -> Logger.log(LogType.DO_ON_NEXT , data))
.switchMap(
num -> Observable.interval(300L, TimeUnit.MILLISECONDS)
.take(10)
.skip(1)
.map(row -> num + " x " + row + " = " + num * row)
).subscribe(
data -> Logger.log(LogType.ON_NEXT, data),
error -> {
},
() -> {
TimeUtil.end();
TimeUtil.takeTime();
}
);
TimeUtil.sleep(5000L);
}
}
/*
doOnNext() | RxComputationThreadPool-1 | 01:18:35.386 | 2
doOnNext() | RxComputationThreadPool-1 | 01:18:35.445 | 3
onNext() | RxComputationThreadPool-3 | 01:18:36.051 | 3 x 1 = 3
onNext() | RxComputationThreadPool-3 | 01:18:36.351 | 3 x 2 = 6
onNext() | RxComputationThreadPool-3 | 01:18:36.651 | 3 x 3 = 9
onNext() | RxComputationThreadPool-3 | 01:18:36.951 | 3 x 4 = 12
onNext() | RxComputationThreadPool-3 | 01:18:37.251 | 3 x 5 = 15
onNext() | RxComputationThreadPool-3 | 01:18:37.551 | 3 x 6 = 18
onNext() | RxComputationThreadPool-3 | 01:18:37.851 | 3 x 7 = 21
onNext() | RxComputationThreadPool-3 | 01:18:38.151 | 3 x 8 = 24
onNext() | RxComputationThreadPool-3 | 01:18:38.451 | 3 x 9 = 27
# 실행시간: 3602 ms
*/
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* switchMap 대신 concatMap을 쓸 경우 비효율적인 검색 예제
*/
public class ObservableSwitchMapExample02 {
public static void main(String[] args) {
TimeUtil.start();
Searcher searcher = new Searcher();
// 사용자가 입력하는 검색어라고 가정한다.
final List<String> keywords = Arrays.asList("M", "Ma", "Mal", "Malay");
Observable.interval(100L, TimeUnit.MILLISECONDS)
.take(4)
.concatMap(data -> { /** concatMap을 사용했기때문에 매번 모든 키워드 검색 결과를 다 가져온다. */
String keyword = keywords.get(data.intValue()); // 데이터베이스에서 조회한다고 가정한다.
return Observable.just(searcher.search(keyword))
.doOnNext(notUse -> System.out.println("===================================================================="))
.delay(1000L, TimeUnit.MILLISECONDS);
})
.flatMap(resultList -> Observable.fromIterable(resultList))
.subscribe(
data -> Logger.log(LogType.ON_NEXT, data),
error -> {},
() ->{
TimeUtil.end();
TimeUtil.takeTime();;
}
);
TimeUtil.sleep(6000L);
}
}
/*
====================================================================
onNext() | RxComputationThreadPool-2 | 01:39:54.089 | Macau
onNext() | RxComputationThreadPool-2 | 01:39:54.092 | Malaysia
onNext() | RxComputationThreadPool-2 | 01:39:54.092 | Maldives
onNext() | RxComputationThreadPool-2 | 01:39:54.093 | Mexico
onNext() | RxComputationThreadPool-2 | 01:39:54.093 | Myanmar
onNext() | RxComputationThreadPool-2 | 01:39:54.093 | Macedonia
====================================================================
onNext() | RxComputationThreadPool-3 | 01:39:55.099 | Macau
onNext() | RxComputationThreadPool-3 | 01:39:55.100 | Malaysia
onNext() | RxComputationThreadPool-3 | 01:39:55.100 | Maldives
onNext() | RxComputationThreadPool-3 | 01:39:55.100 | Macedonia
====================================================================
onNext() | RxComputationThreadPool-4 | 01:39:56.104 | Malaysia
onNext() | RxComputationThreadPool-4 | 01:39:56.104 | Maldives
====================================================================
onNext() | RxComputationThreadPool-5 | 01:39:57.110 | Malaysia
# 실행시간: 4382 ms
*/
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
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* switchMap을 이용한 효율적인 키워드 검색 예제
*/
public class ObservableSwitchMapExample03 {
public static void main(String[] args) {
TimeUtil.start();
Searcher searcher = new Searcher();
// 사용자가 입력하는 검색어라고 가정한다.
final List<String> keywords = Arrays.asList("M", "Ma", "Mal", "Malay");
Observable.interval(100L, TimeUnit.MILLISECONDS)
.take(4)
.switchMap(data -> { /** switchMap을 사용했기 떄문에 마지막 키워드를 사용한 최신 검색 결과만 가져온다. */
String keyword = keywords.get(data.intValue()); // 데이터베이스에서 조회한다고 가정한다.
return Observable.just(searcher.search(keyword))
.doOnNext(notUse -> System.out.println("===================================================================="))
.delay(1000L, TimeUnit.MILLISECONDS);
})
.flatMap(resultList -> Observable.fromIterable(resultList))
.subscribe(
data -> Logger.log(LogType.ON_NEXT, data),
error -> {},
() ->{
TimeUtil.end();
TimeUtil.takeTime();;
}
);
TimeUtil.sleep(2000L);
}
}
/*
====================================================================
====================================================================
====================================================================
====================================================================
onNext() | RxComputationThreadPool-5 | 01:42:37.183 | Malaysia
# 실행시간: 1915 ms
*/
기타 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* range, filter, map을 이용하여 1부터 15까지의 숫자 중에 2의 배수만 필터링 한 후,
* 필터링된 숫자에 제곱한 숫자를 출력하세요.
*/
public class QuizAnswerForChapter050301 {
public static void main(String[] args) {
Observable.range(1, 15)
.filter(number -> number % 2 == 0)
.map(number -> Math.pow(number , 2))
.subscribe(data -> Logger.log(LogType.ON_NEXT, data));
}
}
/*
onNext() | main | 01:48:56.129 | 4.0
onNext() | main | 01:48:56.134 | 16.0
onNext() | main | 01:48:56.134 | 36.0
onNext() | main | 01:48:56.134 | 64.0
onNext() | main | 01:48:56.134 | 100.0
onNext() | main | 01:48:56.134 | 144.0
onNext() | main | 01:48:56.134 | 196.0
*/
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
27
28
29
30
31
32
33
/**
* range, filter, flatMap을 이용하여 2에서 9까지의 구구단 중에서 짝수단만 출력하세요.
*/
public class QuizAnswerForChapter050302 {
public static void main(String[] args) {
Observable.range(2, 8)
.filter(number -> number % 2 == 0)
.flatMap(number -> Observable.range(1, 9)
.map(row -> number + "*" + row + "=" + (row * number)))
.subscribe(data -> Logger.log(LogType.ON_NEXT, data));
}
}
/*
onNext() | main | 01:53:31.011 | 2*1=2
onNext() | main | 01:53:31.013 | 2*2=4
onNext() | main | 01:53:31.013 | 2*3=6
onNext() | main | 01:53:31.014 | 2*4=8
onNext() | main | 01:53:31.014 | 2*5=10
onNext() | main | 01:53:31.014 | 2*6=12
onNext() | main | 01:53:31.014 | 2*7=14
onNext() | main | 01:53:31.014 | 2*8=16
onNext() | main | 01:53:31.014 | 2*9=18
onNext() | main | 01:53:31.014 | 4*1=4
...
onNext() | main | 01:53:31.016 | 8*2=16
onNext() | main | 01:53:31.016 | 8*3=24
onNext() | main | 01:53:31.016 | 8*4=32
onNext() | main | 01:53:31.016 | 8*5=40
onNext() | main | 01:53:31.016 | 8*6=48
onNext() | main | 01:53:31.016 | 8*7=56
onNext() | main | 01:53:31.016 | 8*8=64
onNext() | main | 01:53:31.016 | 8*9=72
*/
이 글은 inflearn에 있는 Kevin의 알기 쉬운 RxJava 1부를 공부하고 작성한 글입니다.
강의영상 링크