-
변환 연산자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