Java响应式编程 第五篇 flatMap vs map

1 作用不同

1.2 映射?展平?

  • map 只执行映射
  • flatMap 既执行映射,也执行展平

什么叫只能执行映射?

我理解是把一个数据执行一个方法,转换成另外一个数据。举个例子:mapper 函数把输入的字符串转换成大写。map()方法执行这个 mapper 函数。

Function<String, String > mapper = String::toUpperCase;
  Flux<String> inFlux = Flux.just("hello", ".", "com");
  Flux<String> outFlux = inFlux.map(mapper);
  // reactor 测试包提供的测试方法
  StepVerifier.create(outFlux)
    .expectNext("HELLO", ".", "COM")
    .expectComplete()
    .verify();

什么叫展平?

mapper 函数把字符串转成大写,然后分割成一个一个字符。

Function<String, Publisher<String>> mapper = s -> Flux.just(s.toUpperCase().split(""));

  Flux<String> inFlux = Flux.just("hello", ".", "com");
  // 这里只能使用 flatMap,因为参数是 Function<T, Publisher<V>> 形式
  Flux<String> outFlux = inFlux.flatMap(mapper);

  List<String> output = new ArrayList<>();
  outFlux.subscribe(output::add);
  // 输出 [H, E, L, L, O, ., C, O, M]
    System.out.println(output);    

请注意,由于来自不同来源的数据项交错,它们在输出中的顺序可能与我们在输入中看到的不同。

1.2 同步?异步?

  • map 是同步的,非阻塞的,1-1(1个输入对应1个输出) 对象转换的;
  • flatMap 是异步的,非阻塞的,1-N(1个输入对应任意个输出) 对象转换的;

当流被订阅(subscribe)之后,映射器对输入流中的元素执行必要的转换(执行上述 mapper 操作)。这些元素中的每一个都可以转换为多个数据项,然后用于创建新的流。

一旦一个由 Publisher 实例表示的新流准备就绪,flatMap 就会急切地订阅。operator 不会等待发布者完成,会继续下一个流的处理,这意味着订阅是非阻塞的。同时也说明 flatMap() 是异步的。

由于管道同时处理所有派生流,因此它们的数据项可能随时进入。结果就是原有的顺序丢失。如果项目的顺序很重要,请考虑改用 flatMapSequential 运算符。

2 方法签名的区别很明显

2.1 方法签名

  • map 参数是 Function<T, U> ,返回是 Flux<U>
  • flatMap 参数是 Function<T, Publisher<V>> 返回是 Flux<V>

举例:

这里只能使用 flatMap,因为参数是 Function<T, Publisher<V>> 形式

Function<String, Publisher<String>> mapper = s -> Flux.just(s.toUpperCase().split(""));

Flux<String> inFlux = Flux.just("hello", ".", "com");
// 这里只能使用 flatMap,因为参数是 Function<T, Publisher<V>> 形式
Flux<String> outFlux = inFlux.flatMap(mapper);

这里只能使用 map,因为参数是 Function<String, String >

Function<String, String > mapper = String::toUpperCase;
Flux<String> inFlux = Flux.just("hello", ".", "com");
// 这里只能使用 map,因为参数是 Function<String, String >
Flux<String> outFlux = inFlux.map(mapper);

此外,看方法签名,可以看出,可以给 map() 传参 Function<T, Publisher<V>>,按照方法签名,它会返回Flux<Publisher<V>>,但它不知道如何处理 Publishers。比如下面的代码:编译不会报错,但是不知道后续怎么处理。

Function<String, Publisher<String>> mapper = s -> Flux.just(s.toUpperCase().split(""));
Flux<String> inFlux = Flux.just("hello", ".", "com");
Flux<Publisher<String>> map = inFlux.map(mapper);

下面的例子来源于 stackoverflow:

使用 map 方法会产生 Mono<Mono<T>>,而使用 flatMap 会产生 Mono<T>。使用 map() 就是给 map 传参了Function<T, Publisher<V>>,它返回的也是 Mono<Publisher<V>>

// Signature of the HttpClient.get method
Mono<JsonObject> get(String url);

// The two urls to call
String firstUserUrl = "my-api/first-user";
String userDetailsUrl = "my-api/users/details/"; // needs the id at the end

// Example with map
Mono<Mono<JsonObject>> result = HttpClient.get(firstUserUrl).
  map(user -> HttpClient.get(userDetailsUrl + user.getId()));
// This results with a Mono<Mono<...>> because HttpClient.get(...)
// returns a Mono

// Same example with flatMap
Mono<JsonObject> bestResult = HttpClient.get(firstUserUrl).
  flatMap(user -> HttpClient.get(userDetailsUrl + user.getId()));
// Now the result has the type we expected

2.3 返回

  • map() 返回一个值的流
  • flatMap() 返回一个流值的流
Flux<String> stringFlux = Flux.just("hello word!");
Function<String, Publisher<String>> mapper = s -> Flux.just(s.toUpperCase().split(""));
// 使用 flatMap() 返回的是 FluxFlatMap.
Flux<String> flatMapFlux = stringFlux.flatMap(mapper);
// 使用 map() 返回的是 FluxMapFuseable
Flux<String> mapFlux = stringFlux.map(s -> s);

flatMapFlux 类型是 FluxFlatMap;也就是说,使用 flatMap() 返回的是 FluxFlatMap.

mapFlux 类型是 FluxMapFuseable。也就是说,使用 map() 返回的是 FluxMapFuseable

FluxMapFuseable 是什么?

FluxFlatMap 是什么?

FluxFlatMap 和 FluxMapFuseable 是什么区别?

各位看官可以一起讨论!

参考链接:

baeldung: [Project Reactor: map() vs flatMap()](https://www.baeldung.com/java-reactor-map-flatmap)

csdn: [map VS flatmap](https://blog.csdn.net/weixx3/article/details/94735767)

geeksforgeeks: [Difference Between map() And flatMap() In Java Stream](https://www.geeksforgeeks.org/difference-between-map-and-flatmap-in-java-stream/)

stackOverFlow: [map vs flatMap in reactor](https://stackoverflow.com/questions/49115135/map-vs-flatmap-in-reactor)

相关文章

第十三章:Java图形用户界面编程

Java图形用户界面(Graphical User Interface,GUI)编程是一种创建交互式应用程序界面的技术。通过使用GUI,开发人员可以创建具有按钮、文本框、复选框等可视化组件的应用程序。...

java课程设计大作业 2048小游戏(设计实现文档+源代码)

目录一、 实现方案3二、 具体代码及程序框图分析4三、 参考资料14一、 实现方案本游戏采用Java语言编写,使用Eclipse编译器, jdk1.7.0_51编译环境。游戏的UI主要运用Java图形...

小高分享(63)Java中的图形、图像与音频

分享兴趣,传播快乐,增长见闻,留下美好!亲爱的您,这里是LearningYard新学苑。今天小编为你带来小高分享(63)Java中的图形、图像与音频欢迎您的访问!Share interests, sp...

Java-GUI编程之绘图

很多程序如各种小游戏都需要在窗口中绘制各种图形,除此之外,即使在开发JavaEE项目时,有时候也必须"动态"地向客户 端生成各种图形、图表,比如 图形验证码、统计图等,这都需要利用AW...

Java-GUI编程之处理位图

如果仅仅绘制一些简单的几何图形,程序的图形效果依然比较单调 。 AWT 也允许在组件上绘制位图, Graphics 提供了 drawlmage() 方法用于绘制位图,该方法需要一个Image参数一一代...

java swing开发的人人五子棋图形界面版.

上次发表了二维数组版,这次是图形界面版,算法是一模一样,就是多了个图形.运行StartGame.java,出现如图界面:然后开始自己和自己下棋:所有的代码结构放入eclipse直接运行!如需全套代码!...