ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 변환 연산자
    BackEnd/RxJava 2023. 7. 7. 07:00
    반응형

    map

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

    package com.itvillage.chapter05.chapter0503;
    
    import com.itvillage.utils.LogType;
    import com.itvillage.utils.Logger;
    import io.reactivex.Observable;
    
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * 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));
        }
    }
    package com.itvillage.chapter05.chapter0503;
    
    import com.itvillage.utils.LogType;
    import com.itvillage.utils.Logger;
    import io.reactivex.Observable;
    
    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));
    
    
        }
    }

     

    flatMap (1)

    • 원본 데이터를 원하는 값으로 변환 후 통지하는 것은 map과 같습니다.
    • map이 1 대 1 변환인 것과 달리 flatMap은 1 대 다 변환하므로 데이터 한 개로 여러 데이터를 통지할 수 있습니다.
    • map은 변환된 데이터를 반환하지만 flatMap은 변환 된 여러개의 데이터를 담고 있는 새로운 Observable을 반환합니다.

    package com.itvillage.chapter05.chapter0503;
    
    import com.itvillage.utils.LogType;
    import com.itvillage.utils.Logger;
    import io.reactivex.Observable;
    
    /**
     * FlapMap을 이용한 1 대 다 mapping 예제
     */
    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));
        }
    }
    package com.itvillage.chapter05.chapter0503;
    
    import com.itvillage.utils.LogType;
    import com.itvillage.utils.Logger;
    import io.reactivex.Observable;
    
    /**
     * flapMap을 이용한 구구단의 2단 출력 예제
     */
    public class ObservableFlatMapExample02 {
        public static void main(String[] args){
            Observable.range(2, 1)
                    .flatMap(
                            num -> Observable.range(1, 9)
                                             .map(row -> num + " * " + row + " = " + num * row)
                    )
                    .subscribe(data -> Logger.log(LogType.ON_NEXT, data));
        }
    }

     

    flatMap (2)

    • 원본 데이터와 변환된 데이터를 조합해서 새로운 데이터를 통지합니다.
    • 즉, Observable에 원본 데이터 + 변환된 데이터 = 최종 데이터를 실어서 반환합니다.

    package com.itvillage.chapter05.chapter0503;
    
    import com.itvillage.utils.LogType;
    import com.itvillage.utils.Logger;
    import io.reactivex.Observable;
    
    /**
     * FlatMap 두번째 유형을 이용한 구구단의 2단 출력 예제
     */
    public class ObservableFlatMapExample03 {
        public static void main(String[] args) {
            Observable.range(2, 1)
                    .flatMap(
                            data -> Observable.range(1, 9),
                            (sourceData, transformedData) ->
                                    sourceData + " * " + transformedData + " = " + sourceData * transformedData
                    )
                    .subscribe(data -> Logger.log(LogType.ON_NEXT, data));
    
        }
    }

     

    concatMap

    • flatMap과 마찬가지로 받은 데이터를 변환하여 새로운 Observable로 반환합니다.
    • 반환된 새로운 Observable을 하나씩 순서대로 실행하는 것이 flatMap과 다릅니다.
    • 즉, 데이터의 처리 순서는 보장하지만 처리중인 Observable의 처리가 끝나야 다음 Observable이 실행되므로 처리 성능에는 영향을 줄 수 있습니다.

    package com.itvillage.chapter05.chapter0503;
    
    import com.itvillage.utils.LogType;
    import com.itvillage.utils.Logger;
    import com.itvillage.utils.TimeUtil;
    import io.reactivex.Observable;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * 순서를 보장해주는 concatMap 예제
     * 순차적으로 실행되기때문에 flatMap보다 느리다.
     */
    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 + " * " + row + " = " + num * row)
                    ).subscribe(
                            data -> Logger.log(LogType.ON_NEXT, data),
                            error -> {},
                            () -> {
                                TimeUtil.end();
                                TimeUtil.takeTime();
                            }
                    );
    
            TimeUtil.sleep(5000L);
        }
    }

     

    switchMap

    • concatMap과 마찬가지로 받은 데이터를 변환하여 새로운 Observable로 반환합니다.
    • concatMap과 다른 점은 switchMap은 순서를 보장하지만 새로운 데이터가 통지되면 현재 처리중이던 작업을 바로 중단합니다.
    • 여러 개의 발행된 값 중에 마지막에 들어온 값만 처리하고자 할 때 유용합니다.

    package com.itvillage.chapter05.chapter0503;
    
    import com.itvillage.utils.LogType;
    import com.itvillage.utils.Logger;
    import com.itvillage.utils.TimeUtil;
    import io.reactivex.Observable;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * 원본 소스의 처리 속도가 빨라서 현재 처리 중이던 작업을 중단하는 예제
     */
    public class ObservableSwitchMapExample01 {
        public static void main(String[] args) throws InterruptedException {
            System.out.println("# start : " +TimeUtil.getCurrentTimeFormatted());
            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 + " * " + row + " = " + num * row)
                    )
                    .subscribe(data -> Logger.log(LogType.ON_NEXT, data));
    
            Thread.sleep(5000);
        }
    }
    package com.itvillage.chapter05.chapter0503;
    
    import com.itvillage.utils.LogType;
    import com.itvillage.utils.Logger;
    import com.itvillage.utils.TimeUtil;
    import io.reactivex.Observable;
    import io.reactivex.Observer;
    
    import java.util.*;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 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)
                    .doOnNext(data -> Logger.log(LogType.DO_ON_NEXT, data))
                    .switchMap(data -> { /** switchMap을 사용했기 때문에 마지막 키워드를 사용한 최신 검색 결과만 가져온다 */
                        String keyword = keywords.get(data.intValue()); // 데이터베이스에서 조회한다고 가정한다.
    
                        return Observable.just(searcher.search(keyword))
                                .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);
        }
    }

     

    groupBy

    • 하나의 Observable을 여러 개의 새로운 GroupedByObservable로 만듭니다.
    • 원본 Observable의 데이터를 그룹별로 묶는다기보다는 각각의 데이터들이 그룹에 해당하는 Key를 가지게 됩니다.
    • GroupedByObservable은 getKey()를 통해 구분된 그룹을 알 수 있습니다.

    package com.itvillage.chapter05.chapter0504;
    
    import com.itvillage.common.Car;
    import com.itvillage.common.CarMaker;
    import com.itvillage.common.SampleData;
    import com.itvillage.utils.LogType;
    import com.itvillage.utils.Logger;
    import io.reactivex.Observable;
    import io.reactivex.observables.GroupedObservable;
    
    /**
     * Group으로 묶은 데이터들 중에서 filter를 이용해 필터링한 Group의 데이터만 출력하는 예제
     */
    public class ObservableGroupByExample02 {
        public static void main(String[] args) {
            Observable<GroupedObservable<CarMaker, Car>> observable =
                    Observable.fromIterable(SampleData.carList).groupBy(Car::getCarMaker);
    
            observable.subscribe(
                    groupedObservable ->
                            groupedObservable
                                    .filter(car -> groupedObservable.getKey().equals(CarMaker.CHEVROLET))
                                    .subscribe(
                                            car -> Logger.log(
                                                    LogType.PRINT, "Group: "
                                                            + groupedObservable.getKey()
                                                            + "\t Car name: " + car.getCarName())
                                    )
            );
    
        }
    }

     

    toList

    • 통지 되는 데이터를 모두 List에 담아 통지합니다.
    • 원본 Observable에서 완료 통지를 받는 즉시 리스트를 통지합니다.
    • 통지되는 데이터는 원본 데이터를 담은 리스트 하나이므로 Single로 반환됩니다.

    package com.itvillage.chapter05.chapter0504;
    
    import com.itvillage.utils.LogType;
    import com.itvillage.utils.Logger;
    import io.reactivex.Observable;
    import io.reactivex.Single;
    
    import java.util.List;
    
    /**
     * 각각의 통지 데이터를 List로 변환해서 Single로 한번만 통지하는 예제
     */
    public class ObservableToListExample01 {
        public static void main(String[] args) {
            Single<List<Integer>> single = Observable.just(1, 3, 5, 7, 9)
                                                    .toList();
    
            single.subscribe(data -> Logger.log(LogType.ON_NEXT, data));
        }
    }
    package com.itvillage.chapter05.chapter0504;
    
    import com.itvillage.common.Car;
    import com.itvillage.common.SampleData;
    import com.itvillage.utils.LogType;
    import com.itvillage.utils.Logger;
    import io.reactivex.Observable;
    import io.reactivex.Single;
    
    import java.util.List;
    
    /**
     * 각각의 통지될 Car 객체를 List로 변환해서 Single로 한번만 통지하는 예제
     */
    public class ObservableToListExample02 {
        public static void main(String[] args) {
            Observable.fromIterable(SampleData.carList)
                    .toList()
                    .subscribe(carList -> Logger.log(LogType.ON_NEXT, carList));
        }
    }

     

    toMap

    • 통지 되는 데이터를 모두 Map에 담아 통지합니다.
    • 원본 Observable에서 완료 통지를 받는 즉시 Map을 통지합니다.
    • 이미 사용중인 key(키)를 또 생성하면 기존에 있던 key(키)와 value(값)을 덮어씁니다.
    • 통지되는 데이터는 원본 데이터를 담은 Map 하나이므로 Single로 반환됩니다.

    package com.itvillage.chapter05.chapter0504;
    
    import com.itvillage.utils.LogType;
    import com.itvillage.utils.Logger;
    import io.reactivex.Observable;
    import io.reactivex.Single;
    
    import java.util.Map;
    
    /**
     * 통지된 데이터에서 map의 키를 생성한 후, 각각의 키별로 원본 통지 데이터를 매핑해서 Map으로 반환하는 예제
     */
    public class ObservableToMapExample01 {
        public static void main(String[] args) {
            Single<Map<String, String>> single =
                    Observable.just("a-Alpha", "b-Bravo", "c-Charlie", "e-Echo")
                            .toMap(data -> data.split("-")[0]); // 반환값은 Map의 key가 된다.
    
            single.subscribe(map -> Logger.log(LogType.ON_NEXT, map));
        }
    }
    package com.itvillage.chapter05.chapter0504;
    
    import com.itvillage.utils.LogType;
    import com.itvillage.utils.Logger;
    import io.reactivex.Observable;
    import io.reactivex.Single;
    
    import java.util.Map;
    
    /**
     * 원본 데이터를 변환한 값과 각각의 key를 매핑하여 Map으로 통지하는 예제
     */
    public class ObservableToMapExample02 {
        public static void main(String[] args) {
            Single<Map<String, String>> single = Observable.just("a-Alpha", "b-Bravo", "c-Charlie", "e-Echo")
                    .toMap(
                        data -> data.split("-")[0],
                        data -> data.split("-")[1]
                    );
    
            single.subscribe(map -> Logger.log(LogType.ON_NEXT, map));
        }
    }

     

     

     

    [참고 자료]

    반응형

    'BackEnd > RxJava' 카테고리의 다른 글

    에러 처리 연산자  (0) 2023.07.08
    결합 연산자  (1) 2023.07.08
    필터링 연산자  (0) 2023.07.06
    Flowable/Observable 생성 연산자  (0) 2023.07.02
    Single, Maybe, Completable  (0) 2023.07.02

    댓글

Designed by Tistory.