275. Java Stream API - flatMap 操作:展开一对多的关系,拉平你的流!

275. Java Stream API - flatMap 操作:展开一对多的关系,拉平你的流!


🧠 背景:我们为什么需要 flatMap

假设我们有以下结构:

  • 每个 Country 拥有多个 City
  • 每个 City 有一个人口数 population

我们的目标是:统计所有城市的总人口数

最直接的写法当然是嵌套 for 循环

java 复制代码
int totalPopulation = 0;
for (Country country : countries) {
    for (City city : country.cities()) {
        totalPopulation += city.population();
    }
}
System.out.println("Total population = " + totalPopulation);

📌 输出:

java 复制代码
Total population = 24493

虽然有效,但 Java 8 之后我们有了更优雅的方式:使用流 + flatMap 来处理一对多的关系


🔁 用 flatMap 优雅替代嵌套循环

✅ 定义模型结构

java 复制代码
record City(String name, int population) {}
record Country(String name, List<City> cities) {}

✅ 初始化数据

java 复制代码
City newYork = new City("New York", 8_258);
City losAngeles = new City("Los Angeles", 3_821);
Country usa = new Country("USA", List.of(newYork, losAngeles));

City london = new City("London", 8_866);
City manchester = new City("Manchester", 568);
Country uk = new Country("United Kingdom", List.of(london, manchester));

City paris = new City("Paris", 2_103);
City marseille = new City("Marseille", 877);
Country france = new Country("France", List.of(paris, marseille));

List<Country> countries = List.of(usa, uk, france);

🚀 使用 flatMap 重写统计逻辑

java 复制代码
int totalPopulation = countries.stream()
                               .flatMap(country -> country.cities().stream())  // 展开所有城市
                               .mapToInt(City::population)                     // 提取人口
                               .sum();                                         // 累加总人口

System.out.println("Total population = " + totalPopulation);

📌 输出:

java 复制代码
Total population = 24493

🔍 flatMap 是如何工作的?

flatMap 是两个操作的组合:

步骤 1️⃣:映射(map)

java 复制代码
country -> country.cities().stream()

这一步将每个 Country 映射为它的城市流,得到的是一个 Stream<Stream<City>>(流的流)。


步骤 2️⃣:展平(flat)

flatMap自动帮你把多个子流合并为一个连续的扁平流Stream<City>),这样你就可以对所有城市统一处理!

🎯 类比图示:

java 复制代码
Stream<Country>  ---映射--->  Stream<Stream<City>>
                     |
                     +--->  展平(flatten)--->  Stream<City>

📚 延伸案例:Map 结构的 flatMap

假设我们有一个 Continent 类型,它包含一个 Map:

java 复制代码
record Continent(Map<String, Country> countries) {}

此时,如果你想从 Continent 中提取所有国家,可以这样写:

java 复制代码
Function<Continent, Stream<Country>> continentToCountry =
    continent -> continent.countries().values().stream();

再进一步,还可以这样嵌套 flatMap

java 复制代码
int total = continents.stream()
                      .flatMap(continent -> continent.countries().values().stream())
                      .flatMap(country -> country.cities().stream())
                      .mapToInt(City::population)
                      .sum();

🧠 小结:flatMap 用法口诀

用法场景 对应方法
一对一映射(每个元素 → 单个新值) .map()
一对多映射(每个元素 → 多个新值) .flatMap()
提取嵌套集合中的内容并扁平化 .flatMap()
转换成基础类型流(int/long/double .mapToInt()

🧪 练习建议(课堂可选)

❓ 问题:下面代码的输出是什么?

java 复制代码
List<String> words = List.of("java", "stream", "api");

List<Character> chars = words.stream()
                             .flatMap(word -> word.chars().mapToObj(c -> (char) c))
                             .toList();

System.out.println(chars);

🎯 答案:

java 复制代码
[j, a, v, a, s, t, r, e, a, m, a, p, i]
相关推荐
cll_8692418919 分钟前
一个好看的Wordpress博客文字css样式
前端·css·ui
糖果店的幽灵13 分钟前
langgraph分支之 - 动态分支(Dynamic Branch)
java·前端·javascript·人工智能·langgraph
meilindehuzi_a23 分钟前
Workflow 与 Agent 有什么区别:从 LangChain 流水线到智能体决策
前端·人工智能
止语Lab2 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活2 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
吃糖的小孩2 小时前
从只读页面到可控真实探针:我如何给 Owner Console 加手动诊断
前端
谷无姜2 小时前
为什么你的性能优化无效?可能是"木桶效应"在作祟
前端·性能优化
雾非雾2 小时前
《基于具身交互智能数字人技术:如何打造AI数字人宣讲平台"红厅智播”》
前端
程序员天天困2 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn2 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端