一、FlatMap函數介紹
FlatMap是函數式編程領域中的一個概念,它是指將一個嵌套多層的數據結構展開成一個一維的數據結構。在Java 8中,我們可以使用Stream的flatMap方法來實現這個功能。
flatMap函數可以將一個Stream對象中的元素進行轉換,Stream中的元素可以是任何類型(基本類型和對象類型),轉換後的結果可以是一個Stream對象或數組、集合等任意類型的對象。
public<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
從上述方法簽名可以看到,flatMap函數需要傳入一個函數式介面Function,這個函數式介面會將Stream中的每個元素轉換成一個Stream對象,最後將所有的Stream對象合併成一個Stream對象。
二、FlatMap函數使用示例
下面我們通過幾個例子來更好地理解flatMap函數的用法。
1. 將一個字元串數組中的所有字元拼接成一個字元串
假設我們有一個字元串數組String[] strs = {“hello”, “world”, “java”},我們想將其中的所有字元拼接成一個字元串,我們可以使用Arrays.stream將字元串數組轉換為Stream對象,然後使用flatMap將每個字元串轉換成一個字元(Stream<Character>),最後使用Collectors.joining方法將所有的字元拼接成一個字元串:
String[] strs = {"hello", "world", "java"};
String result = Arrays.stream(strs)
.flatMap(str -> str.chars().mapToObj(c -> (char) c))
.map(String::valueOf)
.collect(Collectors.joining());
System.out.println(result); //輸出"helloworldjava"
在上述代碼中,我們使用flatMap將字元串轉換成字元Stream,然後使用map將字元轉換成字元串Stream,最後使用Collectors.joining將所有字元串拼接成一個字元串。
2. 將一個家庭對象中的所有成員的愛好合併成一個Set集合
假設我們有一個類似下面的家庭類:
class Family {
private List<Person> members;
//省略getter和setter
}
class Person {
private String name;
private Set<String> hobbies;
//省略getter和setter
}
現在我們需要將這個家庭中所有成員的愛好合併成一個Set集合,我們可以通過Stream流來處理這個問題,代碼如下:
Family family = new Family();
family.setMembers(Arrays.asList(
new Person("Tom", new HashSet<>(Arrays.asList("reading", "swimming"))),
new Person("Jack", new HashSet<>(Arrays.asList("swimming", "running"))),
new Person("Lucy", new HashSet<>(Arrays.asList("reading", "singing")))
));
Set<String> hobbies = family.getMembers()
.stream()
.flatMap(person -> person.getHobbies().stream())
.collect(Collectors.toSet());
System.out.println(hobbies); //輸出"[running, reading, swimming, singing]"
在上述代碼中,我們使用flatMap將每個Person對象的愛好Set集合轉換成一個Stream對象,然後使用Collectors.toSet將所有Stream對象合併成一個Set集合。
3. 將二維數組轉換成一維數組
假設我們有一個二維整數數組int[][] nums = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}},我們需要將它轉換成一個一維整數數組,我們可以使用Stream的flatMap方法將二維數組轉換成Stream<Integer>,之後使用toArray將其轉換成一維整數數組,代碼如下:
int[][] nums = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int[] result = Arrays.stream(nums)
.flatMapToInt(Arrays::stream)
.toArray();
System.out.println(Arrays.toString(result)); //輸出"[1, 2, 3, 4, 5, 6, 7, 8, 9]"
在上述代碼中,我們使用flatMap將二維數組轉換成Stream對象,然後使用flatMapToInt將Stream<Integer[]>轉換成Stream<int[]>,最後使用toArray方法將Stream<int[]>轉換成int[]數組。
三、FlatMap函數的優勢和適用場景
FlatMap函數的主要優勢在於它可以幫助我們將一個嵌套多層的數據結構展開成一個一維的數據結構,降低了代碼的複雜度。
FlatMap適用於任何需要展平(將多維轉換為一維)的場合。最典型的場合有:
- 將多個Stream集合合併成一個Stream集合
- 將子集合合併成一個更大的集合
- 將集合中的元素映射為不同的類型或結構
- 將集合中的元素展開為更小的集合
四、FlatMap函數的一些小技巧
在使用flatMap函數的時候,我們可以通過一些技巧來使代碼更加簡潔易懂。
1. 使用Stream.empty()代替null判斷
在flatMap函數中,我們需要將一個元素轉換為Stream對象,如果這個元素為null,我們可以使用Stream.empty()來代替這個null值,避免出現空指針異常。
List<String> list = Arrays.asList("java", null, "python");
List<Character> result = list.stream()
.flatMap(s -> {
if (s == null) {
return Stream.empty();
} else {
return s.chars().mapToObj(c -> (char) c);
}
})
.collect(Collectors.toList());
System.out.println(result); //輸出"[j, a, v, a, p, y, t, h, o, n]"
2. 使用Optional對象代替null值
在使用flatMap轉換一個元素時,如果這個元素有可能為null,我們可以將其封裝為Optional對象,避免出現空指針異常。
List<String> list = Arrays.asList("java", null, "python");
List<Character> result = list.stream()
.flatMap(s -> Optional.ofNullable(s).map(str -> str.chars().mapToObj(c -> (char) c)).orElse(Stream.empty()))
.collect(Collectors.toList());
System.out.println(result); //輸出"[j, a, v, a, p, y, t, h, o, n]"
在上述代碼中,我們使用Optional.ofNullable將元素封裝為Optional對象,然後使用map方法將其中的非空值轉換為Stream對象,最後使用orElse返回一個Stream對象,這個Stream對象為空。
3. 使用Collectors.flatMapping()函數
在Java 9中,引入了新的函數flatMapping,可以更加簡便的實現flatMap函數的功能,使用方法如下:
public static <T, U, R> Collector<T, ?, R> flatMapping(Function<? super T, ? extends Stream<? extends U>> mapper,
Collector<? super U, A, R> downstream)
從函數簽名可以看到,這個函數需要傳入兩個參數:mapper和downstream,mapper和flatMap函數中的Function參數作用相同,生成Stream對象,downstream參數則決定最終的收集器類型,代碼如下:
List<String> list = Arrays.asList("java", "python", "scala");
List<Character> result = list.stream()
.collect(Collectors.flatMapping(s -> s.chars().mapToObj(c -> (char) c), Collectors.toList()));
System.out.println(result); //輸出"[j, a, v, a, p, y, t, h, o, n, s, c, a, l, a]"
在上述代碼中,我們先使用flatMap將每個字元串轉換成字元Stream,最後使用Collectors.toList將所有字元收集到一個List對象中。
五、總結
FlatMap函數是Stream API中非常重要的一個函數,它可以幫助我們將一個嵌套多層的數據結構展開成一個一維的數據結構。在實際的開發中,我們會經常用到這個函數,理解並掌握其用法和使用技巧可以讓我們的代碼更加簡潔易懂,提高開發效率。
原創文章,作者:HDWRY,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/361868.html