What is flatMap()?
# Stream<String[]>
# Stream<Stream<String>>
# String[][]
[
[1, 2],
[3, 4],
[5, 6]
]
它由一个 2 级 Stream
或一个二维数组组成 。
在 Java 8 中,我们可以使用 flatMap
将上述 2 级 Stream
转换为一级 Stream
或将 二维数组转换为 一维数组。
# Stream<String>
# String[]
[1, 2, 3, 4, 5, 6]
简言之, flatmap
方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接
起来成为一个流。
看一个简单的例子: 使用flatMap
找出单词列表中各不相同的字符
Why flat a Stream?
处理包含多个级别的 Stream ,比如 Stream<String[]>
或 Stream<List<LineItem>>
或 Stream<Stream<String>>
想 将 2 级 Stream 扁平化为一级,如 Stream<String>
或 Stream<LineItem>
,这样就可以轻松地循环 Stream 并对其进行处理。
来看个简单的功能实现,以及常犯的一些错误。
需求: 有 {"a", "b"}, {"c", "d"}, {"e", "f"}
三个数组,要求输出 除去a
之后的数据
/**
* filter out the a and print out all the characters
*/
private static void filterAndPrintCharacters() {
String[][] array = new String[][]{{"a", "b"}, {"c", "d"}, {"e", "f"}};
// convert array to a stream
Stream<String[]> stream = Arrays.stream(array);
// array to a stream [same result]
Stream<String[]> array1 = Stream.of(array);
log.info("==========错误的方式一===============");
// x is a String[], not String!
List<String[]> result = stream.filter(x -> !x.equals("a"))
.collect(Collectors.toList());
log.info(String.valueOf(result.size()));
result.forEach(x -> log.info(Arrays.toString(x)));
log.info("==========错误的方式二===============");
List<String[]> result1 = Arrays.stream(array).filter(x -> {
for (String s : x) { // really?
if (s.equals("a")) {
return false;
}
}
return true;
}).collect(Collectors.toList());
log.info(String.valueOf(result1.size()));
result1.forEach(x -> log.info(Arrays.toString(x)));
log.info("============正确的方式 flatMap=============");
log.info("============先测试转换成一维数组=============");
// [a, b, c, d, e, f]
String[] objects = Arrays.stream(array)
.flatMap(Stream::of)
.toArray(String[]::new);
Arrays.stream(objects).forEach(x -> log.info("|---->{}", x));
log.info("============开始处理=============");
List<String> collect = Arrays.stream(array)
.flatMap(Stream::of)
.filter(x -> !x.equals("a"))
.collect(Collectors.toList());
collect.forEach(x -> log.info(x));
log.info("============处理结束=============");
}
我们先看看:
[错误的方式一]
filter(x -> !x.equals("a")) // x 是数组 ,而非字符串
[错误的方式二]
x -> {
for (String s : x) { // really?
if (s.equals("a")) {
return false;
}
}
return true;
} // 会把整个 [a, b] 过滤出去,而非我们想要过滤的 a
[正确的方式 ]
// flatMap 将二维数组转换成意味数组, 或者可以说是从 Stream<String[]> 转换成Stream<String>.
String[][] array = new String[][]{{"a", "b"}, {"c", "d"}, {"e", "f"}};
// Java 8
String[] result = Stream.of(array) // Stream<String[]>
.flatMap(Stream::of) // Stream<String>
.toArray(String[]::new); // [a, b, c, d, e, f]
Arrays.stream(objects).forEach(x -> log.info("|---->{}", x));
接下来我们就可以很轻松地过滤出来 a了, 就得到了一下最终版本
List<String> collect = Arrays.stream(array)
.flatMap(Stream::of)
.filter(x -> !x.equals("a"))
.collect(Collectors.toList());
collect.forEach(x -> log.info(x));
【小结】
Stream#flatMap
可以将 2 levels Stream 转换成 1 level Stream.
Stream<String[]> -> flatMap -> Stream<String>
Stream<Set<String>> -> flatMap -> Stream<String>
Stream<List<String>> -> flatMap -> Stream<String>
Stream<List<Object>> -> flatMap -> Stream<Object>
Demo
需求: 使用 stream
将List转换为对象流,每个对象都包含一组书籍,使用flatMap
生成包含所有对象中所有书籍的流。过滤掉包含单词python的书,并收集一个Set以删除重复的书。