Stream 流中 flatMap 方法详解

1. flatMap() 到底是啥?

flatMap()Stream 里的中间操作,它的作用可以分两步理解:

  • 第一步:对流里的每个元素,先**映射(转换)**成一个 Stream
  • 第二步:把多个子流拍平成一个大的扁平流。

简单记忆map() 是一对一,flatMap() 是一对多。


2. 基础用法拆解

来看个例子:

List list = List.of("Hello World", "Java Stream");

List result = list.stream()
    .map(s -> s.split(" "))   // 转成 String[] 流
    .flatMap(Arrays::stream)    // 拍平成单层流
    .collect(Collectors.toList());

System.out.println(result);

执行过程解析

步骤 操作 结果
map() 每个元素转成数组 [["Hello", "World"], ["Java", "Stream"]]
flatMap() 拍平成单层流 ["Hello", "World", "Java", "Stream"]

3. map() vs flatMap() 的区别

我们用个更直观的对比例子:

List list = List.of("a,b,c", "d,e,f");

// map()
List mapResult = list.stream()
    .map(s -> s.split(","))  // 每个元素变成 String[]
    .collect(Collectors.toList());

System.out.println(mapResult);
// 输出: [[a, b, c], [d, e, f]]  (嵌套数组)

// flatMap()
List flatMapResult = list.stream()
    .flatMap(s -> Arrays.stream(s.split(",")))
    .collect(Collectors.toList());

System.out.println(flatMapResult);
// 输出: [a, b, c, d, e, f] (扁平化的一层)

总结

功能 map() flatMap()
结果 返回嵌套结构(StreamList等) 返回单层扁平结构
常见用途 单层对象转换 嵌套结构拍平、集合嵌套处理
结果类型 Stream / Stream Stream
示例转换 "A B" → ["A", "B"] [["A", "B"], ["C", "D"]] → ["A", "B", "C", "D"]

4. 高阶实战场景

4.1. 嵌套集合扁平化

List> numbers = List.of(
    List.of(1, 2, 3),
    List.of(4, 5, 6),
    List.of(7, 8, 9)
);

List flatList = numbers.stream()
    .flatMap(List::stream)
    .collect(Collectors.toList());

System.out.println(flatList);
// 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9]

4.2. 字符串拆分

假设我们有一组句子,想拆成单词:

List sentences = List.of("Java is cool", "Stream API is powerful");

List words = sentences.stream()
    .flatMap(s -> Arrays.stream(s.split(" ")))
    .collect(Collectors.toList());

System.out.println(words);
// 输出: [Java, is, cool, Stream, API, is, powerful]

4.3. 模拟 SQL JOIN

我们模拟多表连接(类似 SQL 的笛卡尔积):

List names = List.of("Tom", "Jerry", "Mike");
List hobbies = List.of("Coding", "Gaming", "Reading");

List results = names.stream()
    .flatMap(name -> hobbies.stream().map(hobby -> name + " loves " + hobby))
    .collect(Collectors.toList());

results.forEach(System.out::println);

输出

Tom loves Coding
Tom loves Gaming
Tom loves Reading
Jerry loves Coding
Jerry loves Gaming
Jerry loves Reading
Mike loves Coding
Mike loves Gaming
Mike loves Reading

5. Optional 配合 flatMap()

Optional 也有 flatMap()!特别适合处理嵌套 Optional:

Optional optionalName = Optional.of("Java");

Optional upperName = optionalName
    .flatMap(name -> Optional.of(name.toUpperCase()));

System.out.println(upperName.orElse("No Name"));
// 输出: JAVA

如果用 map()

Optional> nestedOptional = optionalName.map(name -> Optional.of(name.toUpperCase()));
System.out.println(nestedOptional);
// 输出: Optional[Optional[JAVA]]  (嵌套了两层)

6. 常见坑 & 注意事项

1️⃣ 避免空指针

如果 flatMap() 操作的是嵌套集合,务必确保子集合不为 null

List> data = List.of(
    List.of("Java", "Python"),
    null // ❗️注意这里有null
);

List result = data.stream()
    .filter(Objects::nonNull) // 先过滤掉null
    .flatMap(list -> list == null ? Stream.empty() : list.stream())
    .collect(Collectors.toList());

System.out.println(result);

2️⃣ 性能考虑

flatMap() 频繁拍平大数据集合时性能可能受影响,考虑分批次处理,或者用 parallelStream()


3️⃣ 嵌套层级太多

如果嵌套层次太多(List>>),可以链式多次 flatMap()

List>> deepNestedList = List.of(
    List.of(List.of("A", "B"), List.of("C")),
    List.of(List.of("D", "E"))
);

List flatResult = deepNestedList.stream()
    .flatMap(List::stream)
    .flatMap(List::stream)
    .collect(Collectors.toList());

System.out.println(flatResult);
// 输出: [A, B, C, D, E]

7. 终极总结

特点 map() flatMap()
转换关系 一对一转换 一对多(Stream 扁平化)
结果类型 Stream> Stream
常用场景 数据转换,简单映射 嵌套集合、字符串拆解
Optional 嵌套处理 返回嵌套 Optional 解开嵌套 Optional


总结

通过本文的学习,我们深入了解了 flatMap() 在 Java Stream 中的强大功能。它不仅能够处理多层嵌套数据、拆分字符串、模拟 SQL JOIN,更能让你的代码更加简洁优雅,避免冗余的嵌套结构。

无论你是数据处理,还是解决复杂的集合操作,掌握 flatMap() 都将是你提升编码能力的关键一步。希望通过本文,大家能够更高效地利用这个工具,优化自己的代码结构,让代码不仅“能用”,更要“好用”。

如果你在学习过程中有任何疑问,或者有更多 flatMap() 的应用场景,欢迎在评论区留言,我们一起讨论!别忘了点赞收藏分享给更多需要的朋友哦!

你可能感兴趣的:(Java,开发语言,java,stream流)