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]
相关推荐
方也_arkling38 分钟前
别名路径联想提示。@/统一文件路径的配置
前端·javascript
毕设源码-朱学姐40 分钟前
【开题答辩全过程】以 基于web教师继续教育系统的设计与实现为例,包含答辩的问题和答案
前端
web打印社区1 小时前
web-print-pdf:突破浏览器限制,实现专业级Web静默打印
前端·javascript·vue.js·electron·html
橘子师兄1 小时前
C++AI大模型接入SDK—ChatSDK封装
开发语言·c++·人工智能·后端
RFCEO1 小时前
前端编程 课程十三、:CSS核心基础1:CSS选择器
前端·css·css基础选择器详细教程·css类选择器使用方法·css类选择器命名规范·css后代选择器·精准选中嵌套元素
@ chen1 小时前
Spring事务 核心知识
java·后端·spring
Amumu121382 小时前
Vuex介绍
前端·javascript·vue.js
We་ct2 小时前
LeetCode 54. 螺旋矩阵:两种解法吃透顺时针遍历逻辑
前端·算法·leetcode·矩阵·typescript
2601_949480062 小时前
【无标题】
开发语言·前端·javascript
css趣多多2 小时前
Vue过滤器
前端·javascript·vue.js