在 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>>或者流中嵌套流的情况。mapflatMap可以将嵌套的结构“展平”,使得元素以一个扁平的流展现。
7. 总结
flatMap- 它通常用于处理嵌套结构的数据,比如将
List<List<T>>转换为一个单一的流Stream<T>。 - 通过正确使用
flatMap,你可以在处理复杂数据时,获得更高效、清晰的代码。










