[java] RxJava에서 언제 map vs flatMap을 사용합니까?

언제 RxJava 에서 mapvs 를 사용 합니까?flatMap

예를 들어 JSON이 포함 된 파일을 JSON이 포함 된 문자열에 매핑하려고합니다.

를 사용 map하여 Exception어떻게 든 처리해야합니다 . 그러나 어떻게? :

Observable.from(jsonFile).map(new Func1<File, String>() {
    @Override public String call(File file) {
        try {
            return new Gson().toJson(new FileReader(file), Object.class);
        } catch (FileNotFoundException e) {
            // So Exception. What to do ?
        }
        return null; // Not good :(
    }
});

를 사용하면 flatMap훨씬 더 장황하지만 문제를 Observables다른 것으로 선택하고 오류를 처리하면 다시 시도 할 수 있습니다.

Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
    @Override public Observable<String> call(final File file) {
        return Observable.create(new Observable.OnSubscribe<String>() {
            @Override public void call(Subscriber<? super String> subscriber) {
                try {
                    String json = new Gson().toJson(new FileReader(file), Object.class);

                    subscriber.onNext(json);
                    subscriber.onCompleted();
                } catch (FileNotFoundException e) {
                    subscriber.onError(e);
                }
            }
        });
    }
});

나는의 단순함을 좋아 map하지만 flatmap(자세한 것은 아닙니다)의 오류 처리 . 나는 떠 다니는 것에 대한 모범 사례를 보지 못했고 이것이 실제로 어떻게 사용되고 있는지 궁금합니다.



답변

map한 이벤트를 다른 이벤트로 변환하십시오.
flatMap하나의 이벤트를 0 개 이상의 이벤트로 변환합니다. (이것은 IntroToRx 에서 가져온 것입니다 )

json을 객체로 변환하려면 map을 사용하는 것으로 충분합니다.

FileNotFoundException을 다루는 것은 또 다른 문제입니다 (map 또는 flatmap을 사용하면이 문제가 해결되지 않습니다).

예외 문제를 해결하려면 확인되지 않은 예외로 처리하십시오. RX가 onError 핸들러를 호출합니다.

Observable.from(jsonFile).map(new Func1<File, String>() {
    @Override public String call(File file) {
        try {
            return new Gson().toJson(new FileReader(file), Object.class);
        } catch (FileNotFoundException e) {
            // this exception is a part of rx-java
            throw OnErrorThrowable.addValueAsLastCause(e, file);
        }
    }
});

flatmap과 정확히 동일한 버전 :

Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
    @Override public Observable<String> call(File file) {
        try {
            return Observable.just(new Gson().toJson(new FileReader(file), Object.class));
        } catch (FileNotFoundException e) {
            // this static method is a part of rx-java. It will return an exception which is associated to the value.
            throw OnErrorThrowable.addValueAsLastCause(e, file);
            // alternatively, you can return Obersable.empty(); instead of throwing exception
        }
    }
});

flatMap 버전에서는 오류 인 새로운 Observable을 반환 할 수도 있습니다.

Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
    @Override public Observable<String> call(File file) {
        try {
            return Observable.just(new Gson().toJson(new FileReader(file), Object.class));
        } catch (FileNotFoundException e) {
            return Observable.error(OnErrorThrowable.addValueAsLastCause(e, file));
        }
    }
});


답변

FlatMap은 맵과 매우 유사하게 작동하지만 차이점은 적용하는 함수 가 관찰 가능 자체를 리턴하므로 비동기 작업에 대한 맵에 완벽하게 적합하다는 것입니다.

실용적인 의미에서 Map Apply 함수는 연결된 응답을 통해 변환을 수행합니다 (Observable을 반환하지 않음). FlatMap apply 함수는을 반환하지만 Observable<T>, 메소드 내에서 비동기 호출을 수행하려는 경우 FlatMap이 권장됩니다.

요약:

  • 지도는 T 유형의 객체를 반환합니다
  • FlatMap은 Observable을 반환합니다.

http://blog.couchbase.com/why-couchbase-chose-rxjava-new-java-sdk 에서 명확한 예를 볼 수 있습니다 .

Couchbase Java 2.X Client는 Rx를 사용하여 편리한 방식으로 비동기 호출을 제공합니다. Rx를 사용하기 때문에 메소드 맵과 FlatMap이 있으며, 문서의 설명은 일반적인 개념을 이해하는 데 도움이 될 수 있습니다.

오류를 처리하려면 susbcriber에서 onError를 대체하십시오.

Subscriber<String> mySubscriber = new Subscriber<String>() {
    @Override
    public void onNext(String s) { System.out.println(s); }

    @Override
    public void onCompleted() { }

    @Override
    public void onError(Throwable e) { }
};

이 문서를 보는 것이 도움이 될 수 있습니다 : http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/

RX의 오류를 관리하는 방법에 대한 좋은 소스는 https://gist.github.com/daschl/db9fcc9d2b932115b679 에서 찾을 수 있습니다.


답변

귀하의 경우 1 개의 입력과 1 개의 출력 만 있기 때문에 맵이 필요합니다.

map-제공된 함수는 단순히 항목을 받아들이고 한 번만 방출되는 항목을 반환합니다.

flatMap-제공된 함수는 항목을 승인 한 다음 “Observable”을 리턴합니다. 이는 새 “Observable”의 각 항목이 별도로 더 아래로 방출됨을 의미합니다.

코드가 당신을 위해 일을 정리 할 수 ​​있습니다 :

Observable.just("item1").map( str -> {
    System.out.println("inside the map " + str);
    return str;
}).subscribe(System.out::println);

Observable.just("item2").flatMap( str -> {
    System.out.println("inside the flatMap " + str);
    return Observable.just(str + "+", str + "++" , str + "+++");
}).subscribe(System.out::println);

산출:

inside the map item1
item1
inside the flatMap item2
item2+
item2++
item2+++


답변

내가 생각하는 방식 flatMap은 내부에 넣고 싶은 함수가를 map()반환 할 때 사용한다는 것 Observable입니다. 이 경우에도 여전히 사용하려고 map()하지만 실용적이지 않습니다. 이유를 설명하려고 노력하겠습니다.

그러한 경우에 고집하기로 결정 map했다면 Observable<Observable<Something>>. 예를 들어, 가상의 RxGson 라이브러리를 사용했다면 (그냥 단순히 반환하는 대신) 메소드 Observable<String>에서 를 반환하면 다음과 같습니다.toJson()String

Observable.from(jsonFile).map(new Func1<File, Observable<String>>() {
    @Override public Observable<String>> call(File file) {
        return new RxGson().toJson(new FileReader(file), Object.class);
    }
}); // you get Observable<Observable<String>> here

이 시점 subscribe()에서 그러한 관찰 가능한 것은 까다로울 것 입니다. 그것 안에 Observable<String>당신은 다시 subscribe()가치를 얻는 데 필요한 것을 얻을 것입니다. 실용적이지 않고보기에 좋지 않습니다.

따라서 유용한 한 가지 아이디어는이 관측 가능한 관측 가능 항목을 “평평하게”하는 것입니다 (이름이 _flat_Map 인 곳을 볼 수 있습니다). RxJava는 옵저버 블을 평탄화하는 몇 가지 방법을 제공하며 단순성을 위해 병합 이 원하는 것을 가정 수 있습니다 . 병합은 기본적으로 많은 관측 가능 항목을 가져 와서 방출 할 때마다 방출합니다. (많은 사람들은 스위치 가 더 나은 기본값 이라고 주장 것입니다. 그러나 하나의 값만 내 보내면 어쨌든 상관 없습니다.)

이전 스 니펫을 수정하면 다음과 같이됩니다.

Observable.from(jsonFile).map(new Func1<File, Observable<String>>() {
    @Override public Observable<String>> call(File file) {
        return new RxGson().toJson(new FileReader(file), Object.class);
    }
}).merge(); // you get Observable<String> here

구독 (또는 맵핑 또는 필터링 또는 …)을 구독하면 String값을 얻을 수 있으므로 훨씬 유용 합니다. (또한 merge()RxJava에는 이러한 변형이 존재하지 않지만 병합 개념을 이해하면 작동 방식을 이해하기를 바랍니다.)

따라서 기본적으로 merge()이것들은 아마도 map()관측 값 반환에 성공했을 때만 유용 하기 때문에 이것을 반복해서 입력 할 필요가 없으므로 flatMap()속기로 작성되었습니다. 매핑 함수를 일반적인 방식으로 적용 map()하지만 나중에 반환 된 값을 내보내는 대신 “평평하게”(또는 병합)합니다.

이것이 일반적인 사용 사례입니다. Rx를 어디에서나 사용하는 코드베이스에서 가장 유용하며 옵저버 블을 반환하는 많은 메서드가 있으며 옵저버 블을 반환하는 다른 메서드와 체인을 연결하려고합니다.

사용 된 경우에는 map()방출 된 하나의 값만에서 방출 된 onNext()다른 값으로 변환 할 수 있기 때문에 유용합니다 onNext(). 그러나 여러 값으로 변환 할 수 없으며 전혀 값이 없거나 오류가 없습니다. 그리고 akarnokd 가 그의 대답에 썼 듯이 (그리고 아마도 그는 일반적으로 나보다 훨씬 똑똑하지만 적어도 RxJava에 관해서는) 당신에게서 예외를 던져서는 안됩니다 map(). 그래서 그 대신 사용할 수있는 flatMap()

return Observable.just(value);

모든 것이 잘되면

return Observable.error(exception);

무언가가 실패했을 때.
자세한 내용은 https://stackoverflow.com/a/30330772/1402641에 대한 답변을 참조하십시오.


답변

문제는 RxJava에서 언제 map vs flatMap을 사용합니까? . 간단한 데모가 더 구체적이라고 생각합니다.

방출 된 항목을 다른 유형으로 변환하려는 경우 파일을 문자열로 변환하면 map 및 flatMap이 모두 작동 할 수 있습니다. 그러나 나는 더 명확하기 때문에지도 연산자를 선호합니다.

그러나 어떤 곳에서는 flatMap마술 일을 map할 수 있지만 할 수는 없습니다. 예를 들어, 사용자 정보를 얻고 싶지만 사용자가 로그인 할 때 먼저 ID를 가져와야합니다. 분명히 두 개의 요청이 필요하며 순서대로되어 있습니다.

의 시작하자.

Observable<LoginResponse> login(String email, String password);

Observable<UserInfo> fetchUserInfo(String userId);

다음은 두 가지 방법으로, 하나는 로그인을 반환 Response하고 다른 하나는 사용자 정보를 가져 오는 방법입니다.

login(email, password)
        .flatMap(response ->
                fetchUserInfo(response.id))
        .subscribe(userInfo -> {
            // get user info and you update ui now
        });

보시다시피 flatMap 함수가 적용되면 처음에는 사용자 ID를 가져온 Response다음 사용자 정보를 가져옵니다. 두 개의 요청이 완료되면 UI 업데이트 또는 데이터베이스에 데이터 저장과 같은 작업을 수행 할 수 있습니다.

그러나 사용 map하면 멋진 코드를 작성할 수 없습니다. 한마디로 flatMap요청을 직렬화하는 데 도움이 될 수 있습니다.


답변

여기에 간단 엄지 손가락 규칙 나는 나를 사용하는 경우와 같이 결정하는 데 도움을 사용하는 것이 flatMap()이상 map()수신의에서가 Observable.

map변환 을 사용하기로 결정 했다면 변환 코드를 작성하여 객체를 올바르게 반환합니까?

변환의 최종 결과로 돌아 오는 것이 다음과 같은 경우 :

  • 관찰 할 수없는 객체를 사용하면 그냥을 사용map() 합니다. 그리고 map()그 객체를 Observable에 싸서 방출합니다.

  • Observable객체는, 당신은 사용하십시오flatMap() . 그리고 flatMap()Observable을 풀고, 반환 된 객체를 선택하고, 자신의 Observable로 감싸서 방출합니다.

예를 들어 input param의 Titled Cased String 객체를 반환하는 titleCase (String inputParam) 메소드가 있다고 가정 해보십시오. 이 메소드의 리턴 유형은 String또는 Observable<String>입니다.

  • 의 반환 유형 titleCase(..)이 단순한 String경우라면map(s -> titleCase(s))

  • 의 반환 형식이있는 경우 titleCase(..)로했다 Observable<String>, 당신은 사용하십시오flatMap(s -> titleCase(s))

그 희망은 명확합니다.


답변

방금 추가하고 싶었습니다 flatMap. 함수 내에서 사용자 정의 Observable을 실제로 사용할 필요가 없으며 표준 팩토리 메소드 / 연산자를 사용할 수 있습니다.

Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
    @Override public Observable<String> call(final File file) {
        try {
            String json = new Gson().toJson(new FileReader(file), Object.class);
            return Observable.just(json);
        } catch (FileNotFoundException ex) {
            return Observable.<String>error(ex);
        }
    }
});

일반적으로 RxJava에서 가능한 한 많은 수의 안전 장치를 배치 했더라도 가능한 경우 onXXX 메소드 및 콜백에서 (런타임-) 예외를 발생시키지 않아야합니다.