徹底搞懂 flatMap!Java 開發者必修的函數式思維核心技巧!
在日常開發中,我們經常面對“集合中的集合”、“對象中的列表”、“嵌套結構處理”這些問題。處理它們,如果你還在用循環套循環,那就真的該了解下 flatMap 了。
Java 8 的 Stream API 中,flatMap 及其變體(如 flatMapToInt、flatMapToLong、flatMapToDouble)提供了一種優雅且高效的方式,將嵌套結構拍平成線性流,極大地提升了處理數據的靈活性和代碼表達力。
flatMap 原理全拆解:打散嵌套的神兵利器
什么是 flatMap?
- flatMap 是一種 中間操作(intermediate operation)
- 它的關鍵作用是:將一個元素轉換為 Stream,再將所有嵌套的 Stream 合并成一個扁平的 Stream
- 用通俗的話說,它是“映射 + 扁平化”的組合操作
應用場景舉例
想象我們有一個訂單列表,每個訂單里有多個商品行項目(line item),而我們需要獲得所有訂單中所有商品的集合。
示例類結構:
package com.icoderoad.stream.flattening;
import java.util.List;
public class Order {
private List<String> lineItems;
public Order(List<String> lineItems) {
this.lineItems = lineItems;
}
public List<String> getLineItems() {
return lineItems;
}
}扁平化提取所有商品:
package com.icoderoad.stream.flattening;
import java.util.*;
import java.util.stream.Collectors;
public class FlatMapExample {
public static void main(String[] args) {
List<Order> orders = Arrays.asList(
new Order(Arrays.asList("item1", "item2")),
new Order(Arrays.asList("item3", "item4"))
);
List<String> allItems = orders.stream()
.flatMap(order -> order.getLineItems().stream())
.collect(Collectors.toList());
System.out.println(allItems); // 輸出: [item1, item2, item3, item4]
}
}多層嵌套也不怕:List<List> 的拍平處理
List<List<Integer>> nested = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5),
Arrays.asList(6, 7, 8)
);
List<Integer> flattened = nested.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(flattened); // 輸出: [1, 2, 3, 4, 5, 6, 7, 8]flatMapToInt:拍平整合為 IntStream
對于需要處理原始 int 類型數據的場景,使用 flatMapToInt 可以避免裝箱拆箱的性能開銷。
package com.icoderoad.stream.flattening;
import java.util.*;
import java.util.stream.IntStream;
public class FlatMapToIntExample {
public static void main(String[] args) {
List<List<Integer>> listOfLists = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5, 6)
);
IntStream intStream = listOfLists.stream()
.flatMapToInt(list -> list.stream().mapToInt(Integer::intValue));
intStream.forEach(System.out::println); // 輸出: 1 2 3 4 5 6
}
}flatMapToLong:處理 Long 類型數組的扁平化
package com.icoderoad.stream.flattening;
import java.util.*;
import java.util.stream.LongStream;
public class FlatMapToLongExample {
public static void main(String[] args) {
List<long[]> longLists = Arrays.asList(
new long[]{1L, 2L, 3L},
new long[]{4L, 5L, 6L}
);
LongStream longStream = longLists.stream()
.flatMapToLong(Arrays::stream);
longStream.forEach(System.out::println); // 輸出: 1 2 3 4 5 6
}
}flatMapToDouble:拍平成 DoubleStream
雖然和前兩個操作類似,但專為浮點數設計:
package com.icoderoad.stream.flattening;
import java.util.*;
import java.util.stream.DoubleStream;
public class FlatMapToDoubleExample {
public static void main(String[] args) {
List<double[]> doubleLists = Arrays.asList(
new double[]{1.1, 2.2, 3.3},
new double[]{4.4, 5.5}
);
DoubleStream doubleStream = doubleLists.stream()
.flatMapToDouble(Arrays::stream);
doubleStream.forEach(System.out::println); // 輸出: 1.1 2.2 3.3 4.4 5.5
}
}flatMap 家族通性總結
方法名 | 輸入結構 | 輸出結構 | 適用場景 |
flatMap |
|
| 泛型嵌套結構打平 |
flatMapToInt |
|
| 整型數據處理(避免裝箱) |
flatMapToLong |
|
| long 類型數據整合 |
flatMapToDouble |
|
| 浮點類型數據整合 |
注意事項:使用 flatMap 時必須了解的幾個點
- 延遲執行(Lazy Evaluation):只有在調用終止操作(如
collect、forEach)時,flatMap 才會真正執行 - 避免 NullPointerException:flatMap 會自動將
null映射為Stream.empty(),這點非常實用 - 映射函數必須返回 Stream 類型:flatMap 接收的 lambda 應該返回 Stream、IntStream、LongStream、DoubleStream 中的一種
- 推薦使用場景:
嵌套集合如 List<List<T>>
類似 JSON 的嵌套結構映射
將多個流拼接為一個流處理
結語:flatMap,不只是一個函數,是思維模式的進化
掌握 flatMap,不只是寫出更簡潔的 Java 代碼,更是對函數式思維的一次深刻練習。
從解構嵌套集合,到轉化為線性處理模型,flatMap 是 Java 函數式編程的精髓之一。它不僅能讓你寫出高性能、高可讀性的代碼,也能讓你對數據結構處理方式有更深入的理解。
下次再看到嵌套數據結構,不要再害怕——用 flatMap 優雅解決!



























