BackEnd/RxJava
변환 연산자
hanseom
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));
}
}
[참고 자료]
반응형