Posts RxJava - 데이터 변환 연산자 (1)
Post
Cancel

RxJava - 데이터 변환 연산자 (1)

데이터 변환 연산자

map

  • 원본 Observable에서 통지하는 데이터를 원하는 값으로 변환 후 통지한다.
  • 변환 전, 후 데이터 타입은 달라도 상관없다.
  • null을 반환하면 NullpointException이 발생하므로 null이 아닌 데이터 하나를 반드시 반환해야 한다.

map

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을 반환한다.

flatMap

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에 원본 데이터 + 변환된 데이터 = 최종 데이터 를 실어서 반환한다.

flapMap second

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이 실행되므로 처리 성능에는 영향을 줄 수 있다.

concatMap

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은 순서를 보장하지만 새로운 데이터가 통지되면 현재 처리중이던 작업을 바로 중단한다.

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부를 공부하고 작성한 글입니다.
강의영상 링크

This post is licensed under CC BY 4.0 by the author.

RxJava - 데이터 필터링 연산자

RxJava - 데이터 변환 연산자 (2)

Comments powered by Disqus.