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]
相关推荐
咖啡八杯12 分钟前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan2 小时前
多Agent之间的区别
后端
kyriewen5 小时前
我手写了一个 EventEmitter,面试官追问了 6 个问题——第 4 个我没答上来
前端·javascript·面试
杨充5 小时前
1.面向对象设计思想
后端
IT_陈寒5 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro5 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
小林攻城狮5 小时前
使用 Transport 节流解决 Vercel AI SDK 流式渲染卡死问题
前端·react.js
要阿尔卑斯吗6 小时前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端
前端缘梦6 小时前
告别 TS 运行时类型漏洞!Zod 完整入门实战教程(前端 / 全栈必备)
前端·react.js·全栈
the_answer6 小时前
Webpack vs Vite 深度对比分析
前端·webpack