[RxJava] 연산자 : Map, flatMap, filter, reduce

이영한·2022년 1월 1일
1

RxJava

목록 보기
2/3
post-thumbnail

RxJava에는 약 400개에 달하는 다양한 연산자가 있다. 그 중 가장 많이 쓰이는 연산자인 Map, flatMap, filter, reduce에 대해 알아보자

1. map

map은 데이터를 변환시켜주는 연산자로 java8 스트림의 map과 비슷하다. Map의 역할은 Observable의 각 데이터를 특정 함수를 적용하여 원하는 Observable로 만드는 것이다.

예를 들어 문자열이 주어질 때 각 길이를 알고 싶다하자. 이 때 Map을 이용할 수 있다.

Observable<String> lengthObservable = Observable.fromArray(new String[]{"apple","banana","melon"});
lengthObservable.map(it -> "length of '" + it + "' is " + it.length()).subscribe(System.out::println);

실행결과는 아래와 같다

실행결과
length of 'apple' is 5
length of 'banana' is 6
length of 'melon' is 5

이제 map의 원형을 살펴보자

    @CheckReturnValue
    @SchedulerSupport(SchedulerSupport.NONE)
    public final <R> Observable<R> map(Function<? super T, ? extends R> mapper) {
        ObjectHelper.requireNonNull(mapper, "mapper is null");
        return RxJavaPlugins.onAssembly(new ObservableMap<T, R>(this, mapper));
    }

Function interface를 인자로 받기 때문에 앞의 예시처럼 람다 형태로 인자를 넣어줄 수 있다.


2. flatMap

flatMap은 데이터를 Observable로 변환시키는 함수를 받고, 각 변환된 Observable에서 나온 데이터를 하나로 취합하여 새로운 Observable을 만드는 함수이다.

글로 쓰니 상당히 복잡한데 마블 다이어그램을 보면 명확하다.

예시에서는 동그라미를 받아 다이아몬드 모양 데이터 두 개를 발행하는 Observable을 만드는 함수를 인자로 받는다. 그리고 각 Observable에서 나온 데이터는 하나의 Observable로 합쳐져서 발행된다.

이때 주의깊게 봐야할 점은 Observable의 발행 순서가 지켜지지 않는다는 것이다. 중간에 파란색 다이아몬드가 초록 다이아몬드보다 먼저 나타나는 것을 볼 수 있다.

숫자를 받아서 2,3으로 나누어 떨어지는지를 알고 싶다고 하자. 이때 flatMap을 사용할 수 있다.

Observable<Integer> divideObservable = Observable.fromArray(new Integer[]{4,9,12});
divideObservable.flatMap(it -> Observable.just("is " + it + " can be divided by 2? : " + (it%2==0) , "is " + it + " can be divided by 3? : " + (it%3 == 0) )).subscribe(System.out::println);

실행결과는 예상대로 4는 2로만, 9는 3으로만, 12는 2,3모두 나누어 떨어진다고 나타난다.

실행결과
is 4 can be divided by 2? : true
is 4 can be divided by 3? : false
is 9 can be divided by 2? : false
is 9 can be divided by 3? : true
is 12 can be divided by 2? : true
is 12 can be divided by 3? : true


3. filter

이름 그대로 필터링을 하는 함수이다.

filter 함수는 map, faltmap과 다르게 Predicate를 인자로 받는다.

    @CheckReturnValue
    @SchedulerSupport(SchedulerSupport.NONE)
    public final Observable<T> filter(Predicate<? super T> predicate) {
        ObjectHelper.requireNonNull(predicate, "predicate is null");
        return RxJavaPlugins.onAssembly(new ObservableFilter<T>(this, predicate));
    }

Predicate는 데이터를 받아서 bool을 return하는 test함수를 가지고 있다. test함수에서 true를 반환하면 filter의 결과물에 포함된다. 반대로 false를 받으면 필터링이 되어 결과물에서 제외된다.

예시로 짝수, 홀수를 판단하기 위해 사용할 수 있다.

 Observable.just(1,2,3,4)
                .filter(it -> it % 2 == 0)
                .subscribe(System.out::println);

실행결과는 짝수인 2,4만 출력된다

실행결과
2
4


4. reduce

Observable에서 나온 데이터를 순서대로 처리하여 하나의 값으로 만들어내는 함수이다.

reduce함수는 BiFunction type의 param을 받는다

    @CheckReturnValue
    @SchedulerSupport(SchedulerSupport.NONE)
    public final Maybe<T> reduce(BiFunction<T, T, T> reducer) {
        ObjectHelper.requireNonNull(reducer, "reducer is null");
        return RxJavaPlugins.onAssembly(new ObservableReduceMaybe<T>(this, reducer));
    }

BiFuction은 두 인자를 받아 특정 값을 리턴한다. 원래 param1, param2, return value의 타입이 달라도 되지만 reduce에서는 각 타입이 모두 같은것을 알 수 있다.

예시로 Int Observable의 각 데이터를 모두 더할 수 있다.

Observable.just(1,2,3,4,5,6,7,8,9,10)
                .reduce((p1, p2) -> p1 + p2)
                .subscribe(System.out::println);

1 부터 10까지 더하니 결과는 55가 나온다

실행결과
55



rxJava의 모든 연산자를 알고 있기에는 그 수가 너무 많아서 쉽지 않다. 차라리 연산자의 기능정도만 알고 있다가 필요할 때 찾아 쓰면 좋을 것 같다.

예를 들면 concatMap이라는 함수가 있다. 앞서 살펴본 flatMap과 같은 역할을 하는데 한 가지 다른점은 데이터 발행의 순서가 보장된다는 것이다. 비동기 연산에서 데이터의 순서보장을 하려면 고려해야할 것들이 많은데 concatMap을 사용한다면 쉽게 구현할 수 있다.

아래 링크에 이 글에서 소개 못한 다른 연산자들의 기능을 간단하게 적어보았다.
https://github.com/20Han/RxJava-Practice/blob/master/operator/AdvancedOperator.md



[참고자료]
https://reactivex.io/documentation/operators/flatmap.html
RxJava 프로그래밍

profile
간단하게 개발하고 싶습니다

0개의 댓글