在 Java 8 中,Stream API 提供了多种操作来处理集合数据,其中 flatMap
1. flatMap
的作用
flatMap
方法与 map
方法类似,都是将流中的每个元素映射成一个新的元素。然而,flatMap
与 map
的关键区别在于它能够将每个元素映射为一个新的流,而不仅仅是一个单一的值。然后,flatMap
会将所有的流合并成一个扁平的流。
简而言之:
map
将每个元素转换为一个值(例如:从一个字符串转换为一个整数)。flatMap
将每个元素转换为一个流(例如:将一个集合转换为一个流),然后将这些流合并成一个扁平的流。
2. flatMap
方法签名
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
T
是输入流中元素的类型。R
是输出流中元素的类型。mapper
是一个函数,它将输入流中的每个元素(类型T
)映射成一个流(Stream<R>
)。该流的元素类型是R
。
3. 使用场景
flatMap
常见的使用场景有:
- 处理包含多个元素的集合,将其转换为单一的流。
- 从嵌套的数据结构(如 List<List<T>>)中提取元素。
- 将一个对象的多个属性转换为流。
4. 示例
4.1 基本示例:从一个列表中展开多个值
假设我们有一个包含多个字符串的列表,我们想要将每个字符串拆分成字符,并生成一个扁平的流。
import java.util.Arrays;
import java.util.List;
public class FlatMapExample {
public static void main(String[] args) {
List<String> words = Arrays.asList("hello", "world", "java");
words.stream()
.flatMap(word -> Arrays.stream(word.split("")))
.forEach(System.out::println);
}
}
输出:
h
e
l
l
o
w
o
r
l
d
j
a
v
a
在这个例子中:
flatMap(word -> Arrays.stream(word.split("")))
:将每个字符串word
使用split
拆分成字符数组,然后转换为流Stream<String>
。flatMap
会将这些字符数组流合并为一个扁平的流(一个Stream<String>
)。- 最终我们获得了所有单个字符的流。
4.2 示例:从 List<List<Integer>> 展平为一个流
假设我们有一个 List<List<Integer>>
,我们希望将嵌套的列表展开成一个单一的流。
import java.util.Arrays;
import java.util.List;
public class FlatMapExample {
public static void main(String[] args) {
List<List<Integer>> listOfLists = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5),
Arrays.asList(6, 7, 8)
);
listOfLists.stream()
.flatMap(List::stream)
.forEach(System.out::println);
}
}
输出:
1
2
3
4
5
6
7
8
在这个例子中:
flatMap(List::stream)
将每个嵌套的列表(List<Integer>
)转换为流(Stream<Integer>
)。flatMap
会将这些流扁平化成一个单一的流Stream<Integer>
,然后打印出所有的整数。
4.3 示例:从多个文件中读取内容并展开
假设你有多个文件,每个文件包含多行数据,你想将所有文件的内容合并为一个流。
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
public class FlatMapExample {
public static void main(String[] args) throws IOException {
List<String> fileNames = Arrays.asList("file1.txt", "file2.txt", "file3.txt");
fileNames.stream()
.flatMap(file -> {
try {
return Files.lines(Paths.get(file));
} catch (IOException e) {
e.printStackTrace();
return Stream.empty(); // 如果有异常,则返回一个空的流
}
})
.forEach(System.out::println);
}
}
在这个例子中:
Files.lines(Paths.get(file))
返回一个文件的每一行构成的流(Stream<String>
)。flatMap
会将所有文件的行流合并成一个单一的流,然后输出所有的行。
5. flatMap
与 map
的区别
map
和 flatMap
的行为非常相似,但是它们有一个显著的区别:
map
:对于每个输入元素,返回一个新的元素。
- 如果你使用
map
对一个Stream<String>
进行操作,将每个字符串转换为一个字符数组(例如:word.split("")
),返回的将是Stream<String[]>
(一个包含多个字符数组的流)。
flatMap
:对于每个输入元素,返回一个流,然后将这些流合并成一个扁平的流。
- 如果你使用
flatMap
对一个Stream<String>
进行操作,将每个字符串转换为字符流,返回的将是Stream<String>
(一个扁平化的流,包含所有的字符)。
例如:
List<String> words = Arrays.asList("hello", "world");
// 使用 map
words.stream()
.map(word -> word.split("")) // 返回一个 Stream<String[]>
.forEach(arr -> System.out.println(Arrays.toString(arr)));
// 使用 flatMap
words.stream()
.flatMap(word -> Arrays.stream(word.split(""))) // 返回一个扁平化的 Stream<String>
.forEach(System.out::println);
6. 常见用法总结
flatMap
主要用于处理嵌套结构,如List<List<T>>
或者流中嵌套流的情况。map
flatMap
可以将嵌套的结构“展平”,使得元素以一个扁平的流展现。
7. 总结
flatMap
- 它通常用于处理嵌套结构的数据,比如将
List<List<T>>
转换为一个单一的流Stream<T>
。 - 通过正确使用
flatMap
,你可以在处理复杂数据时,获得更高效、清晰的代码。