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以删除重复的书。

















