276. Java Stream API - 使用 flatMap 和 mapMulti 清理数据并转换类型

文章目录

  • [276. Java Stream API - 使用 `flatMap` 和 `mapMulti` 清理数据并转换类型](#276. Java Stream API - 使用 flatMap 和 mapMulti 清理数据并转换类型)
      • [🎯 场景需求:清理并转换用户输入的数据](#🎯 场景需求:清理并转换用户输入的数据)
    • [❌ 错误示例:filter + map 是不合适的](#❌ 错误示例:filter + map 是不合适的)
    • [✅ 正确示例:用 `flatMap` 一次性搞定!](#✅ 正确示例:用 flatMap 一次性搞定!)
      • [🔨 示例代码:](#🔨 示例代码:)
      • [✅ 输出:](#✅ 输出:)
    • [🆕 Java 16 新方法:`mapMulti` 更高效!](#🆕 Java 16 新方法:mapMulti 更高效!)
      • [💡 `mapMulti()` 是更高效的替代:](#💡 mapMulti() 是更高效的替代:)
      • [🧩 示例代码:](#🧩 示例代码:)
      • [✅ 输出:](#✅ 输出:)
    • [🧠 `mapMulti()` 是如何工作的?](#🧠 mapMulti() 是如何工作的?)
    • [⚠️ 注意:类型推断语法](#⚠️ 注意:类型推断语法)
    • [🧪 总结对比](#🧪 总结对比)
    • [🧩 延伸建议:泛型类型安全版 `flatParser`](#🧩 延伸建议:泛型类型安全版 flatParser)

276. Java Stream API - 使用 flatMap 和 mapMulti 清理数据并转换类型


🎯 场景需求:清理并转换用户输入的数据

你收到一个 List<String>,每个字符串理论上代表一个整数,但实际上这些字符串可能:

  • 包含空格(如 "3 ")
  • 是空字符串 ""
  • 甚至是 null(这里没有写出,但应当处理)
  • 包含非法字符(如 "abc")

你的目标是:只保留可以成功转换为整数的字符串 ,并将它们转为 List<Integer>。


❌ 错误示例:filter + map 是不合适的

很多人第一反应可能是这样:

java 复制代码
Predicate<String> isANumber = s -> {
    try {
        Integer.parseInt(s);
        return true;
    } catch (NumberFormatException e) {
        return false;
    }
};

List<Integer> ints = strings.stream()
                            .filter(isANumber)         // 检查
                            .map(Integer::parseInt)    // 再转
                            .toList();

但这有两个大问题:

  1. ❌ 重复解析:
    • Integer.parseInt(s) 被调用了两次,浪费性能。
  2. ❌ try-catch 返回布尔值是设计异味:
    • 把异常逻辑用于逻辑判断,可读性差。

✅ 正确示例:用 flatMap 一次性搞定!

你可以用 flatMap 结合 try-catch 实现清洗 + 转换,只要转换成功就返回一个单元素流,失败就返回空流。

🔨 示例代码:

java 复制代码
Function<String, Stream<Integer>> flatParser = s -> {
    try {
        return Stream.of(Integer.parseInt(s));
    } catch (NumberFormatException e) {
        return Stream.empty();  // 解析失败,直接跳过
    }
};

List<String> strings = List.of("1", " ", "2", "3 ", "", "3");

List<Integer> ints = strings.stream()
                            .flatMap(flatParser)
                            .toList();

System.out.println("ints = " + ints);

✅ 输出:

java 复制代码
ints = [1, 2, 3]

🌟 所有无效数据(如 " "、""、"3 ")都被优雅地丢弃了。


🆕 Java 16 新方法:mapMulti 更高效!

虽然 flatMap 很强大,但它会为每个元素都生成一个新的 Stream 对象,性能不如一次性收集高效。

💡 mapMulti() 是更高效的替代:

它通过回调方式,决定是否将元素加入最终结果流,避免了不必要的中间流。


🧩 示例代码:

java 复制代码
List<String> strings = List.of("1", " ", "2", "3 ", "", "3");

List<Integer> ints = strings.stream()
                            .<Integer>mapMulti((string, consumer) -> {
                                try {
                                    consumer.accept(Integer.parseInt(string));
                                } catch (NumberFormatException ignored) {
                                    // 什么也不做,自动丢弃无效数据
                                }
                            })
                            .toList();

System.out.println("ints = " + ints);

✅ 输出:

java 复制代码
ints = [1, 2, 3]

🧠 mapMulti() 是如何工作的?

动作 意义
string 当前流中要处理的元素
consumer.accept(x) 将 x 放入最终的 stream 输出
不调用 accept() 表示此元素无效,不进入最终输出(相当于被过滤)

👀 相比 flatMap,mapMulti 不创建额外的流,适合高性能场景。


⚠️ 注意:类型推断语法

你需要手动告诉编译器输出元素类型:

java 复制代码
.<Integer>mapMulti(...)

如果你省略它:

java 复制代码
.mapMulti(...)

编译器将默认输出 Stream<Object>,这会导致后续类型错误。


🧪 总结对比

特性 flatMap mapMulti
引入版本 Java 8 Java 16
用法 返回 Stream(可能是空) 使用 consumer.accept() 添加元素
性能 为每个元素构建流(较慢) 不生成中间流(更高效)
适合场景 结构映射 & 扁平化 条件过滤 & 数据转换

🧩 延伸建议:泛型类型安全版 flatParser

如果你担心 null 或更复杂的转换,可以将函数封装为更通用版本:

java 复制代码
static <T> Function<String, Stream<T>> safeParse(Function<String, T> parser) {
    return s -> {
        try {
            return Stream.of(parser.apply(s));
        } catch (Exception e) {
            return Stream.empty();
        }
    };
}

使用:

java 复制代码
strings.stream()
       .flatMap(safeParse(Integer::parseInt))
       .toList();
相关推荐
Pniubi4 小时前
HashMap的扩容和树化边界
java·开发语言
Mikko74 小时前
用 Java 复刻了高中时在塞班上玩的「熔岩矿车」:纯 Swing、零依赖、没有一张图片,一个 jar 双击就能玩
java·游戏开发·swing
知了学历孙老师4 小时前
小自考的校考和统考,出题方和难度差在哪
java·大数据·前端
Java爱好狂.4 小时前
Java初学者如何一周速通SpringBoot!
java·spring boot·后端·spring·高并发·架构师·java面试
秋饼5 小时前
LLM API密钥动态轮换实战:Spring Vault + Spring AI + 多Provider路由
java·ai·技术分享·后端开发
gCode Teacher 格码致知5 小时前
Pandas教学-17:Series与Dataframe的关系
开发语言·python·pandas
发量惊人的中年网工6 小时前
分公司访问总部ERP慢怎么办?SD-WAN组网的排查与验收要点
服务器·开发语言·php
下页、再停留6 小时前
【C#桌面客户端系列学习-15】生成可视化图表之折线图、柱状图、饼状图
开发语言·c#
ShineWinsu6 小时前
对于JavaScript:操作元素的解析
开发语言·前端·javascript·面试·web·dom·元素
福兮说7 小时前
JSON 格式正确,数据就可靠吗?我用一份自编配置检查类型与空值
java·前端·json