zl程序教程

您现在的位置是:首页 >  Java

当前栏目

Java8 - Streams flatMap()

2023-02-19 12:20:17 时间

文章目录

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