瞧高手如何用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实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!

相关推荐
国服第二切图仔22 分钟前
Rust开发之使用panic!处理不可恢复错误
开发语言·后端·rust
جيون داد ناالام ميづ35 分钟前
Spring Boot 核心原理(一):基础认知篇
java·spring boot·后端
南囝coding1 小时前
现代Unix命令行工具革命:30个必备替代品完整指南
前端·后端
夏之小星星1 小时前
Springboot结合Vue实现分页功能
vue.js·spring boot·后端
唐僧洗头爱飘柔95271 小时前
【SpringCloud(8)】SpringCloud Stream消息驱动;Stream思想;生产者、消费者搭建
后端·spring·spring cloud·设计思想·stream消息驱动·重复消费问题
韩立学长2 小时前
【开题答辩实录分享】以《自动售货机刷脸支付系统的设计与实现》为例进行答辩实录分享
vue.js·spring boot·后端
cj6341181502 小时前
DBeaver连接本地MySQL、创建数据库表的基础操作
java·后端
程序员爱钓鱼3 小时前
Python编程实战—面向对象与进阶语法 | 属性与方法
后端·python·ipython
程序员爱钓鱼4 小时前
Python编程实战——面向对象与进阶语法 | 构造函数与析构函数
后端·python·ipython
逻极4 小时前
Rust之结构体(Structs):构建自定义数据类型
开发语言·后端·rust