当前位置: 首页 > news >正文

Stream API 新玩法:从 teeing()到 mapMulti()

1. 背景:Stream API 的演进

Java 8 引入 Stream API 以来,Java 的集合处理方式发生了质变。开发者可以用声明式风格实现复杂的数据转换与聚合。然而,随着应用场景多样化,社区逐渐发现一些“尴尬空缺”:

  • 聚合时,往往需要计算两个指标(比如总数与平均值),却只能先 .collect() 一次,再写逻辑合并,代码冗余。
  • 一对多映射场景(比如把一条日志展开成多条事件),过去只能用 flatMap(),但其表达力稍显笨重。

Java 12 开始,JDK 对 Stream 的功能进行了增强,加入了更灵活的收集器和映射器。其中最值得关注的就是 teeing()mapMulti()


2. teeing() 收集器 —— 一次遍历,双份结果

2.1 用途

teeing()Java 12 引入的新收集器。它允许我们在一次流处理过程中,同时执行两个独立的收集操作,并在结束时用一个合并函数把结果“合并”起来。

2.2 传统写法(累赘)

假设我们要计算一组订单的 总金额平均金额

List<Integer> orders = List.of(100, 200, 300, 400);// 传统写法:需要遍历两次
int sum = orders.stream().mapToInt(i -> i).sum();
double avg = orders.stream().mapToInt(i -> i).average().orElse(0);

这里 orders.stream() 被遍历了两次,在大数据场景下显得低效。


2.3 teeing() 写法(优雅)

import static java.util.stream.Collectors.*;var result = orders.stream().collect(teeing(summingInt(Integer::intValue),      // 收集器1:求和averagingInt(Integer::intValue),    // 收集器2:平均(sum, avg) -> String.format("Sum=%d, Avg=%.2f", sum, avg)));System.out.println(result); 
// 输出: Sum=1000, Avg=250.00

一次遍历,得到两个收集结果,再由合并函数包装成最终对象。


2.4 适用场景

  • 统计类场景(如最大值 & 最小值、总数 & 平均值)
  • 报表生成(一次聚合,多维指标输出)
  • 性能敏感场景(避免多次遍历)

3. mapMulti() —— 更强大的 “一对多” 映射

3.1 背景

在 Java 8 的 Stream 中,如果要把一条记录映射成多条,需要 flatMap()

Stream.of("a:b:c", "d:e").flatMap(s -> Arrays.stream(s.split(":"))).forEach(System.out::println);

flatMap() 可以解决问题,但表达能力有限

  • 必须返回一个流 Stream<T>,有时仅仅想用条件判断推送几个元素,却要额外包装。
  • 需要注意额外的对象创建(比如数组或中间 Stream)。

3.2 mapMulti() 简化表达

mapMulti()Java 16 引入,它的思路是:你负责“往下游收集器里塞元素”,JDK 不强制你非得返回一个 Stream。

Stream.of("a:b:c", "d:e").mapMulti((str, downstream) -> {for (String part : str.split(":")) {downstream.accept(part);}}).forEach(System.out::println);

效果等价于上面 flatMap() 示例,但避免了额外的 Arrays.stream()


3.3 高级用法 —— 条件展开

比如:只展开长度大于 1 的子串。

Stream.of("x:yz:abc", "m:n").mapMulti((str, downstream) -> {for (String part : str.split(":")) {if (part.length() > 1) {downstream.accept(part);}}}).forEach(System.out::println);
// 输出: yz, abc

这种写法,逻辑更直观,避免创建一堆中间集合/流。


3.4 适用场景

  • 日志/文本解析:一行可能映射成多条事件。
  • 过滤展开:部分元素不展开,部分元素展开多份。
  • 高性能场景:避免额外 Stream 对象的开销。

4. 对比小结

特性teeing()mapMulti()
引入版本Java 12Java 16
作用一次遍历,双收集结果高效一对多映射
替代两次 .collect() 或手写聚合逻辑flatMap()(但更直观、更高效)
典型应用统计报表、聚合指标日志解析、条件展开

5. flatMap() vs mapMulti() 性能对比

我们写一段小测试,生成一百万条带分隔符的字符串,用 flatMap()mapMulti() 分别展开,观察耗时。

import java.util.*;
import java.util.stream.*;
import java.time.*;public class MapMultiVsFlatMap {public static void main(String[] args) {// 构造一百万条字符串List<String> data = IntStream.range(0, 1_000_000).mapToObj(i -> "a:b:c:d:e").toList();// 测试 flatMaplong t1 = System.currentTimeMillis();long count1 = data.stream().flatMap(s -> Arrays.stream(s.split(":"))).count();long t2 = System.currentTimeMillis();// 测试 mapMultilong t3 = System.currentTimeMillis();long count2 = data.stream().mapMulti((s, downstream) -> {for (String part : s.split(":")) {downstream.accept(part);}}).count();long t4 = System.currentTimeMillis();System.out.printf("flatMap count=%d, time=%dms%n", count1, (t2 - t1));System.out.printf("mapMulti count=%d, time=%dms%n", count2, (t4 - t3));}
}

预期结果(不同机器可能略有差异):

flatMap count=5000000, time=242ms
mapMulti count=5000000, time=181ms

可以看到,在数据量大时,mapMulti() 由于避免了中间 Stream 对象的构建,通常会比 flatMap() 更快一些,且内存占用也更低。


6. 总结

Stream API 在 Java 12 之后不断进化:

  • teeing() 让我们可以一次性得到多个收集结果,既优雅又高效;
  • mapMulti() 则让“一对多”的映射逻辑更加自然简洁

随着这些增强,Java 的函数式编程能力逐渐补齐了短板,在复杂数据处理场景中变得更加顺手。

http://www.dtcms.com/a/351028.html

相关文章:

  • 多种“找不到vcruntime140.dll,无法继续执行代码”提示的解决方法:从原理到实操,轻松修复系统故障
  • 【Delphi】中通过索引动态定位并创建对应窗体类实例
  • CMake构建学习笔记20-iconv库的构建
  • MATLAB在生态环境数据处理与分析中的应用,生态系统模型构建与数值模拟等
  • 简述滚珠丝杆升降机的结构和原理
  • CSS 结构伪类选择器
  • 【BUG排查】调试瑞萨RH850F1KMS1时候随机出现进入到unused_isr
  • 一款基于 .NET 开源、功能强大的 Windows 搜索工具
  • GD32VW553-IOT开发板测评 搭建环境到电灯(QA分享)
  • 使用提供的 YAML 文件在 Conda 中创建环境
  • Conda的配置
  • 实时平台Flink热更新技术——实现不停机升级!
  • Caddy + CoreDNS 深度解析:从功能架构到性能优化实践(上)
  • webrtc音频QOS方法一.1(NetEQ之音频网络延时DelayManager计算补充)
  • 设计模式学习笔记-----抽象策略模式
  • 【Ansible】Ansible部署K8s集群--准备环境--配置网络
  • 主流的 AI Agent 开发框架
  • 论文阅读(四)| 软件运行时配置研究综述
  • 游戏玩家批量多开挂机如何选择:云手机还是模拟器
  • LabVIEW 场效应晶体管仿真实验平台
  • 工业自动化系统架构-(多动子磁悬浮生产流水线 规划调度执行与协调)
  • 从下载到运行:MySQL 详细安装配置完整教程
  • 【Vue3】Cesium实现卫星及无人机轨迹跟踪
  • 大模型入门实战 | 基于 YOLO 数据集微调 Qwen2.5-VL-3B-Instruct 的目标检测任务
  • 数字IC前端设计——DC综合篇(生成filelist.f)
  • ADB 安装教程:如何在 Windows、 Linux 上安装 Android Debug Bridge
  • Java数据结构速成【1】
  • 项目设计文档——爬虫项目(爬取天气预报)
  • Qt——界面美化 QSS
  • 无人机三维路径规划首选算法:RRT_