瞧高手如何用flatMap简化代码!

点击上方"程序员蜗牛g",选择"设为星标"

跟蜗牛哥一起,每天进步一点点

程序员蜗牛g

大厂程序员一枚 跟蜗牛一起 每天进步一点点

33篇原创内容

**

公众号

本篇文章将详细的介绍flatMap函数的各种场景下的使用。

假设你有一个List<List>,而你想要得到一个包含所有字符串的单一列表。大多数开发者会在这里使用flatMap,如下示例:

less 复制代码
List<List<String>> names = List.of(  List.of("张三", "李四"),  List.of("曹查理", "大卫"),  List.of("女娲", "Jerry"));// 使用流(Stream)将嵌套的列表展平为一个单一的字符串列表List<String> flattened = names.stream()  .flatMap(List::stream)  // 将每个子列表中的元素"拉平"到一个流中  .toList();  // 收集结果为一个新列表// 输出展平后的结果System.out.println(flattened);

输出结果

css 复制代码
[张三, 李四, 曹查理, 大卫, 女娲, Jerry]

这是最基础用法。我们没有得到一个Stream<List>,而是将其"扁平化"成了一个单一的Stream。**

**

使用Optional时的flatMap技巧

如果你正在使用一个可能存在也可能不存在的数据时。你可能会用 Optional 来处理这种情况。但是,如果你需要串联多个操作呢?

通常情况下,你会得到嵌套的Optional<Optional>,这看起来就很头大。而这正是flatMap大显身手的地方。如下示例:

vbnet 复制代码
public static void main(String[] args) {  Optional<String> nameOpt = Optional.of("Spring Boot3实战案例200讲");  Optional<Optional<String>> result = nameOpt      .map(FlatMapDemo2::toUpperCase); // ❌ 得到嵌套的 Optional  System.out.println(result);}public static Optional<String> toUpperCase(String s) {  if (s == null || s.isEmpty()) {      return Optional.empty();  }  return Optional.of(s.toLowerCase());}

输出结果

python 复制代码
Optional[Optional[spring boot3实战案例200讲]]

使用flatMap

vbnet 复制代码
Optional<String> nameOpt = Optional.of("Spring Boot3实战案例200讲");Optional<String> result = nameOpt.flatMap(FlatMapDemo2::toUpperCase) ;System.err.println(result) ;

输出结果

css 复制代码
Optional[spring boot3实战案例200讲]

总结:

  • map:会对结果进行包装,从而产生嵌套结构。
  • flatMap:会解开包装,保持简洁。

flatMap替代嵌套循环

嵌套循环,会损害代码的可读性,尤其是在处理多维数据时。而flatMap可以用一种更优雅的方式替代它们。

如下示例:两个列表的笛卡尔积,假设你想要生成产品及其所有颜色组合的列表。

less 复制代码
List<String> products = List.of("IPad", "IPhone", "Book");List<String> colors = List.of("黑色", "白色");List<String> result = products.stream()  .flatMap(product -> colors.stream()          .map(color -> product + " - " + color))  .collect(Collectors.toList());result.forEach(System.out::println);

输出结果

复制代码
IPad - 黑色IPad - 白色IPhone - 黑色IPhone - 白色Book - 黑色Book - 白色

如果不使用flatMap,你就得编写嵌套循环。而使用flatMap后,代码会变得清晰、简洁且具有函数式风格。**

**

flatMap处理API响应

在实际应用中,你经常需要从可能返回列表、可选字段或嵌套结构的API中获取数据。你可以使用flatMap直接转换并扁平化结果,而无需进行空值检查。

如下示例:扁平化API数据,假设有一个User类,其中包含一个返回电话号码列表的方法:

arduino 复制代码
public static void main(String[] args) {  List<User> users = List.of(      new User("Pack_xg", Arrays.asList("111", "222")),      new User("pack", Arrays.asList("333")),      new User("xg", Arrays.asList("444", "555"))      );  List<String> result = users.stream()      .flatMap(user -> user.phones().stream())      .toList() ;  System.out.println(result);}public static record User(String name, List<String> phones) {}

输出结果

csharp 复制代码
[111, 222, 333, 444, 555]

如果你用for循环实现此功能,代码会显得臃肿。而借助flatMap,只需一行代码就能完成转换。**

**

flatMap + filter:秘密武器

大多数开发者会使用filter和map,但很少将它们与flatMap结合使用。而正是这种结合能让你解锁另一层强大功能。如下示例:提取有效电子邮件地址

less 复制代码
List<List<String>> emails = List.of(    List.of("pack_xg@gmail.com", "www.qq.com"),    List.of("pack@yahoo.com"),    List.of("xg@", "xxxooo@qq.com"));List<String> result = emails.stream()    .flatMap(List::stream)    .filter(email -> email.contains("@") && email.contains("."))    .toList() ;System.out.println(result);

输出结果

perl 复制代码
[pack_xg@gmail.com, pack@yahoo.com, xxxooo@qq.com]

我们无需逐个验证嵌套列表中的元素,而是可以在一个操作链中完成扁平化、过滤和收集。

综合示例

你需要获取用户数据;每个用户都有若干订单;每个订单都包含若干产品。而你想要得到一个扁平化的列表,其中包含所有用户购买的所有产品的名称。

准备如下类:

vbnet 复制代码
//商品public record Product(String name) {}//订单public record Order(List<Product> products) {}//用户public record User(String name, List<Order> orders) {}

准备数据

sql 复制代码
List<Product> p1 = List.of(  new Product("iPhone 15"),  new Product("AirPods Pro"));List<Product> p2 = List.of(  new Product("MacBook Pro"));List<Order> o1 = List.of(  new Order(p1),  new Order(p2));User zhangsan = new User("张三", o1);List<Product> lisiProducts1 = List.of(  new Product("iPad Air"),  new Product("Apple Watch"));List<Order> o2 = List.of(    new Order(lisiProducts1));User lisi = new User("李四", o2);User wangwu = new User("王五", List.of());// 所有用户List<User> users = List.of(zhangsan, lisi, wangwu) ;

传统做法

css 复制代码
List<String> productNames = new ArrayList<>();for (User user : users) {  for (Order order : user.orders()) {    for (Product product : order.products()) {      productNames.add(product.name());    }  }}System.err.println(productNames) ;

使用flatMap

scss 复制代码
List<String> productNames = users.stream()  .flatMap(user -> user.orders().stream())  .flatMap(order -> order.products().stream())  .map(Product::name)  .toList() ;

总结:掌握flatMap不仅会让你的代码更简洁------它还会改变你的思维方式。你将不再编写那些千篇一律的循环代码。

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。

关注公众号:woniuxgg,在公众号中回复:笔记 就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!

相关推荐
Victor35643 分钟前
Netty(20)如何实现基于Netty的WebSocket服务器?
后端
缘不易43 分钟前
Springboot 整合JustAuth实现gitee授权登录
spring boot·后端·gitee
Kiri霧1 小时前
Range循环和切片
前端·后端·学习·golang
WizLC1 小时前
【Java】各种IO流知识详解
java·开发语言·后端·spring·intellij idea
Victor3561 小时前
Netty(19)Netty的性能优化手段有哪些?
后端
爬山算法1 小时前
Netty(15)Netty的线程模型是什么?它有哪些线程池类型?
java·后端
白宇横流学长2 小时前
基于SpringBoot实现的冬奥会科普平台设计与实现【源码+文档】
java·spring boot·后端
Python编程学习圈2 小时前
Asciinema - 终端日志记录神器,开发者的福音
后端
bing.shao2 小时前
Golang 高并发秒杀系统踩坑
开发语言·后端·golang
壹方秘境2 小时前
一款方便Java开发者在IDEA中抓包分析调试接口的插件
后端