你什么时候在RxJava中使用map vs flatMap?

Chr*_*rry 172 java mapping rx-java flatmap

你什么时候在RxJava中使用map vs 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 :(
    }
});
Run Code Online (Sandbox Code Playgroud)

使用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);
                }
            }
        });
    }
});
Run Code Online (Sandbox Code Playgroud)

我喜欢地图的简单性,但是flatmap的错误处理(不是冗长).我没有看到任何关于这种浮动的最佳实践,我很好奇这是如何在实践中使用的.

dwu*_*sen 116

map将一个事件转换为另一个 flatMap将一个事件转换为零个或多个事件.(这取自IntroToRx)

由于您希望将json转换为对象,因此使用map应该足够了.

处理FileNotFoundException是另一个问题(使用map或flatmap无法解决此问题).

要解决您的异常问题,只需使用Non checked异常抛出它: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);
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

与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
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

您也可以在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));
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

  • 请注意,`OnErrorThrowable`的构造函数是`private`,你需要使用`OnErrorThrowable.from(e)`. (7认同)
  • 这不会调用`subscriber.onError()`等.我看到的所有例子都以这种方式路由错误.这没关系吗? (2认同)

1va*_*ng0 76

FlatMap的行为与map非常相似,不同之处在于它所应用的函数本身会返回一个observable,因此它非常适合映射异步操作.

在实际意义上,Map应用函数只是对链式响应进行转换(不返回Observable); 而FlatMap函数应用返回一个Observable<T>,这就是为什么如果你计划在方法内进行异步调用,建议使用FlatMap的原因.

摘要:

  • Map返回T类型的对象
  • FlatMap返回一个Observable.

这里可以看到一个明显的例子:http://blog.couchbase.com/why-couchbase-chose-rxjava-new-java-sdk.

Couchbase Java 2.X客户端使用Rx以方便的方式提供异步调用.由于它使用Rx,因此它具有方法map和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) { }
};
Run Code Online (Sandbox Code Playgroud)

查看此文档可能会有所帮助:http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/

有关如何使用RX管理错误的良好来源,访问:https://gist.github.com/daschl/db9fcc9d2b932115b679

  • 总结是错误的。Map 和 FlatMap 返回相同的类型,但它们应用的函数返回不同的类型。 (2认同)

mt.*_*ulu 54

在您的情况下,您需要地图,因为只有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);
Run Code Online (Sandbox Code Playgroud)

输出:

inside the map item1
item1
inside the flatMap item2
item2+
item2++
item2+++
Run Code Online (Sandbox Code Playgroud)


Mar*_*ski 23

我想到的方式是你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
Run Code Online (Sandbox Code Playgroud)

在这一点上,subscribe()对这样一个观察者来说是非常棘手的.在它里面你会得到一个Observable<String>你需要再次subscribe()获得价值的东西.这看起来不实用或不好看.

因此,为了使它有用,一个想法是"展平"这个可观察的可观察对象(你可能会开始看到_flat_Map来自何处).RxJava提供了几种方法来展平observable,为了简单起见,我们假设merge是我们想要的.合并基本上会带来一堆可观察的,并且只要它们中的任何一个发出就会发出.(很多人会认为转换是一个更好的默认值.但是如果你只发出一个值,那么无论如何都无关紧要.)

所以修改我们以前的代码片段,我们会得到:

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
Run Code Online (Sandbox Code Playgroud)

这是非常有用的,因为订阅(或映射,或过滤,或......)您只需获得String价值.(另外,请注意,merge()RxJava中不存在这样的变体,但是如果你理解了merge的想法,那么我希望你也能理解它是如何工作的.)

所以基本上因为merge()当它成功map()返回一个observable并且你不必一遍又一遍地输入它时,它应该只是有用,它flatMap()被创建为一个简写.它像正常map()情况一样应用映射函数,但稍后它不会发出返回值,而是"展平"(或合并)它们.

这是一般用例.它在代码库中最有用,它使用Rx整个地方,并且你有许多方法返回observables,你想要与其他返回observables的方法链接.

在您的用例中,它恰好也很有用,因为map()只能将发出的一个值转换onNext()为另一个值onNext().但它无法将其转换为多个值,根本没有值或错误.正如akarnokd在他的回答中所写的那样(并且请注意,他比我更聪明,可能一般而言,但至少在涉及到RxJava时)你不应该从你的问题中抛出异常map().所以你可以使用flatMap()

return Observable.just(value);
Run Code Online (Sandbox Code Playgroud)

当一切顺利的时候,但是

return Observable.error(exception);
Run Code Online (Sandbox Code Playgroud)

什么东西都失败了
有关完整代码段,请参阅他的答案:https://stackoverflow.com/a/30330772/1402641


CoX*_*ier 17

问题是你什么时候在RxJava中使用map vs flatMap?.我认为一个简单的演示更具体.

如果要将发出的项目转换为其他类型,在将文件转换为String的情况下,map和flatMap都可以工作.但我更喜欢地图操作员,因为它更清楚.

但是在某些地方,flatMap可以做神奇的工作而map不能.例如,我想获得用户的信息,但我必须在用户登录时首先获取他的ID.显然我需要两个请求,它们是有序的.

让我们开始.

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

Observable<UserInfo> fetchUserInfo(String userId);
Run Code Online (Sandbox Code Playgroud)

这里有两个方法,一个用于返回登录Response,另一个用于获取用户信息.

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

如您所见,在函数flatMap中应用,首先我获取用户id Response然后获取用户信息.当两个请求完成后,我们可以完成更新UI或将数据保存到数据库中的工作.

但是,如果你使用map你不能写这么好的代码.总之,flatMap可以帮助我们序列化请求.


kar*_*iks 16

下面是一个简单的拇指规则,我用帮我决定的时候使用flatMap()map()的的Rx的Observable.

一旦你决定采用map转换,你就会编写转换代码来返回一些Object吗?

如果您转换的最终结果是:

  • 一个不可观察的对象然后你只使用map().并将map()该对象包装在Observable中并发出它.

  • 一个Observable对象,然后你就用了flatMap().并flatMap()解开Observable,选择返回的对象,用自己的Observable包装并发出它.

比方说,我们有一个方法titleCase(String inputParam),它返回输入参数的Titled Cased String对象.此方法的返回类型可以是StringObservable<String>.

  • 如果返回类型titleCase(..)仅仅是String,那么你会使用map(s -> titleCase(s))

  • 如果返回类型titleCase(..)为是Observable<String>,那么你会使用flatMap(s -> titleCase(s))

希望澄清一下.


aka*_*okd 11

我只是想添加它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);
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

通常,您应该尽可能避免从onXXX方法和回调中抛出(运行时)异常,即使我们在RxJava中放置了尽可能多的安全措施.


ndo*_*ori 6

在该场景中使用map,您不需要新的Observable.

您应该使用Exceptions.propagate,它是一个包装器,因此您可以将这些已检查的异常发送到rx机制

Observable<String> obs = 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) {
            throw Exceptions.propagate(t); /will propagate it as error
        } 
    } 
});
Run Code Online (Sandbox Code Playgroud)

然后,您应该在订户中处理此错误

obs.subscribe(new Subscriber<String>() {
    @Override 
    public void onNext(String s) { //valid result }

    @Override 
    public void onCompleted() { } 

    @Override 
    public void onError(Throwable e) { //e might be the FileNotFoundException you got }
};); 
Run Code Online (Sandbox Code Playgroud)

有一个很好的帖子:http://blog.danlew.net/2015/12/08/error-handling-in-rxjava/