在Java 8中,Stream.map()和Stream.flatMap()方法之间有什么区别?


当前回答

flatMap()还利用了流的部分延迟求值。它将读取第一个流,只有在需要时才会进入下一个流。这里详细解释了这种行为:flatMap保证是懒惰的吗?

其他回答

请仔细阅读这篇文章以获得一个清晰的概念,

map vs flatMap:

要从列表中返回每个单词的长度,我们将执行如下操作。

简短版本如下

当我们收集两个列表时,如下所示

没有flat map =>[1,2],[1,1] =>[[1,2],[1,1]]这里在一个列表中放置了两个列表,因此输出将是包含列表的列表

使用flat map =>[1,2],[1,1] =>[1,2,1,1],这里两个列表被平铺,只有值被放在列表中,因此输出将是只包含元素的列表

基本上,它将所有对象合并为一个

##详细版本已给出如下:-

例如:-考虑一个列表[" STACK ", " OOOVVVER "],我们试图返回一个列表像[" STACKOVER "](从该列表中只返回唯一的字母) 最初,我们将执行如下操作,从[" STACK ", " OOOVVVER "]返回一个列表[" STACKOVER "]

public class WordMap {
  public static void main(String[] args) {
    List<String> lst = Arrays.asList("STACK","OOOVER");
    lst.stream().map(w->w.split("")).distinct().collect(Collectors.toList());
  }
}

这里的问题是,传递给map方法的Lambda为每个单词返回一个字符串数组,因此map方法返回的流实际上是流类型,但我们需要的是流来表示字符流,下面的图像说明了这个问题。

图一:

你可能会想,我们可以用flatmap来解决这个问题,让我们看看如何用map和arrays。stream来解决这个问题 首先,你需要一个字符流而不是数组流。有一个叫做Arrays.stream()的方法,它将接受一个数组并生成一个流,例如:

String[] arrayOfWords = {"STACK", "OOOVVVER"};
Stream<String> streamOfWords = Arrays.stream(arrayOfWords);
streamOfWords.map(s->s.split("")) //Converting word in to array of letters
    .map(Arrays::stream).distinct() //Make array in to separate stream
    .collect(Collectors.toList());

上面的方法仍然不起作用,因为我们现在得到了一个流的列表(更准确地说,流>)。相反,我们必须首先将每个单词转换为一个单独的字母数组,然后将每个数组转换为一个单独的流

通过使用flatMap,我们应该能够修复这个问题如下:

String[] arrayOfWords = {"STACK", "OOOVVVER"};
Stream<String> streamOfWords = Arrays.stream(arrayOfWords);
streamOfWords.map(s->s.split("")) //Converting word in to array of letters
    .flatMap(Arrays::stream).distinct() //flattens each generated stream in to a single stream
    .collect(Collectors.toList());

flatMap不是用流而是用流的内容来映射每个数组。在使用map(Arrays::stream)时生成的所有单独流被合并成一个流。图B说明了使用flatMap方法的效果。将其与图A中的map进行比较。 图B

flatMap方法允许您用另一个流替换流的每个值,然后将所有生成的流连接到单个流中。

传递给流的函数。Map必须返回一个对象。这意味着输入流中的每个对象都会导致输出流中的一个对象。

传递给流的函数。flatMap为每个对象返回一个流。这意味着该函数可以为每个输入对象返回任意数量的对象(包括none)。然后将结果流连接到一个输出流。

简单的答案。

映射操作可以生成流的流。前流<流<整数> >

flatMap操作只会产生流。前流<整数>

我想举两个例子来说明更实际的观点: 第一个使用地图的例子:

@Test
public void convertStringToUpperCaseStreams() {
    List<String> collected = Stream.of("a", "b", "hello") // Stream of String 
            .map(String::toUpperCase) // Returns a stream consisting of the results of applying the given function to the elements of this stream.
            .collect(Collectors.toList());
    assertEquals(asList("A", "B", "HELLO"), collected);
}

在第一个例子中没有什么特别的,一个函数被应用来返回大写的String。

第二个使用flatMap的例子:

@Test
public void testflatMap() throws Exception {
    List<Integer> together = Stream.of(asList(1, 2), asList(3, 4)) // Stream of List<Integer>
            .flatMap(List::stream)
            .map(integer -> integer + 1)
            .collect(Collectors.toList());
    assertEquals(asList(2, 3, 4, 5), together);
}

在第二个例子中,传递了一个List流。它不是一个整数流! 如果必须使用转换函数(通过map),则首先必须将流平展为其他类型的流(整数流)。 如果flatMap被移除,则返回以下错误:对于参数类型List, int,操作符+未定义。 不可能在整数列表上应用+ 1 !

通过阅读所有的信息,简单的理解方法是:

如果你有一个元素的平面列表,请使用map: [0,1,2,3,4,5] 如果你有一个元素的列表,请使用flatMap:[[1,3,5],[2,4,6]]。这意味着,在映射操作应用于每个元素之前,您的列表需要被平铺