ChatGPT解决这个技术问题 Extra ChatGPT

Java 8 中的 map() 和 flatMap() 方法有什么区别?

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

类型签名有点讲述了整个故事。 map :: Stream T -> (T -> R) -> Stream RflatMap :: Stream T -> (T -> Stream R) -> Stream R
fwiw,这些类型签名甚至看起来都不像 Java。 (我知道,我知道——但要说它讲述了“整个故事”wrt map/flatMap 假设有很多关于新的和改进的“Java++”的知识)
@michael该类型签名看起来像Haskell,而不是Java。但尚不清楚实际的 Java 签名是否更具可读性:<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
哈,是的,我指的是“实际的 Java”。与 C++ 一样,任何在 90 年代开始使用现代 Java 的人几乎都认不出来(就像我一样,这两种语言都是如此)。只是回复评论,该方法签名几乎不能说明“整个故事”,至少现在不再,没有额外的说明(或在评论者的情况下,翻译)。
也就是说,map 的映射器 lambda 返回 RflatMap 的映射器 lambda 返回 R (Stream<R>) 的 StreamflatMap 的映射器返回的流被有效连接。否则,mapflatMap 都返回 Stream<R>;不同之处在于映射器 lambda 的返回值,RStream<R>

S
Stuart Marks

mapflatMap 都可以应用于 Stream<T> 并且它们都返回 Stream<R>。不同之处在于 map 运算为每个输入值生成一个输出值,而 flatMap 运算为每个输入值生成任意数量(零个或多个)值。

这反映在每个操作的参数中。

map 操作采用 Function,它为输入流中的每个值调用并产生一个结果值,该值被发送到输出流。

flatMap 操作采用一个在概念上想要消耗一个值并产生任意数量的值的函数。但是,在 Java 中,方法返回任意数量的值是很麻烦的,因为方法只能返回零或一个值。可以想象一种 API,其中 flatMap 的映射器函数接受一个值并返回一个数组或一个 List 值,然后将其发送到输出。鉴于这是流库,表示任意数量的返回值的一种特别恰当的方法是映射器函数本身返回一个流!映射器返回的流中的值从流中排出并传递给输出流。每次调用映射器函数返回的值的“团块”在输出流中根本没有区别,因此输出被称为“扁平化”。

如果要发送零值,典型用途是 flatMap 的映射器函数返回 Stream.empty(),如果要返回多个值,则返回 Stream.of(a, b, c) 之类的东西。但是当然可以返回任何流。


在我看来,flatMap 操作与 flat 完全相反。再一次,把它留给计算机科学家来改变一个术语。就像一个函数是“透明的”,这意味着你看不到它所做的任何事情,只能看到结果,而通俗地说你希望一个过程是透明的意味着你希望它的每一部分都被看到。
@coladict 尝试从不同的角度查看它:这不是一个透明的案例,您可以通过它看到内部工作原理,但整个功能本身对您来说是透明的,即不可见 - 同时仍然在做他们的工作并让您看到自己的东西'重新合作。在这种情况下,“flat”指的是“nested”的反义词,flatmap 通过展平来移除一个嵌套级别。
@coladict“透明”的东西多年来一直在吃我的脑袋。很高兴知道至少另一个人有同样的感觉。
扁平化来自将 2 级结构转变为单级结构,请参阅 Dici 的答案以获取示例 stackoverflow.com/a/26684582/6012102
这是对 flatMap 最好的解释。这就是所有点击的原因:映射器返回的流中的值从流中排出并传递到输出流。每次调用映射器函数返回的“团块”值在输出流中根本没有区别,因此输出被称为“扁平化”。谢谢!
a
asgs

Stream.flatMap,正如它的名字一样,是 mapflat 操作的组合。这意味着您首先将函数应用于元素,然后将其展平。 Stream.map 仅将函数应用于流而不展平流。

要了解流包含的展平内容,请考虑像 [ [1,2,3],[4,5,6],[7,8,9] ] 这样具有“两个级别”的结构。展平这意味着将其转换为“一级”结构:[ 1,2,3,4,5,6,7,8,9 ]


简单甜美
哈哈,公平地说,我仍然很惊讶看到这个问题获得了多少流量。另一个有趣的观察是,我写这个答案已经快 5 年了,并且有一个相当一致的投票模式,即我的答案得到的每个答案都会得到大约两个投票。这是惊人的一致。
这怎么不是公认的答案,感谢您直截了当并举了一个非常简单的例子
我相信这个答案比公认的答案更简洁,在逻辑上也得到了更好的解释。
喜欢你简短而甜蜜的例子。谢谢!
m
m02ph3u5

我想举 2 个例子来获得 更多 实用的观点:
第一个使用 map 的例子:

@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);
}

在第一个示例中没有什么特别之处,应用 Function 以返回大写的 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 流。 它不是整数流!
如果必须使用转换函数(通过映射),那么首先必须将流展平为其他东西(a整数流)。
如果移除 flatMap,则返回以下错误:运算符 + 未定义参数类型 List, int。 strong>
不可能对 List 个整数应用 + 1!


@PrashanthDebbadwar 我认为您最终会得到 Stream<Integer> 流而不是 Integer 流。
O
OneCricketeer

请完整阅读帖子以获得清晰的想法,

地图与平面地图:

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

下面给出的简短版本

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

没有平面地图 => [1,2],[1,1] => [[1,2],[1,1]] 这里有两个列表放在一个列表中,所以输出将是包含列表的列表

使用平面地图 => [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 为每个单词返回一个 String 数组,所以 map 方法返回的流实际上是 Stream 类型,但是我们需要的是 Stream 来表示一个字符流,下图说明了问题。

图一:

https://i.stack.imgur.com/0GRsT.png

你可能会想,我们可以使用 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());

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

通过使用 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());

https://i.stack.imgur.com/yf3vz.png

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


很好的图解说明。
@TechDog 我很欣赏你的例子。我被困在试图找到如何将线条分成单词并流式传输它们的过程中。
感谢您根据带有插图的实际示例进行解释。对理解很有帮助。
H
Hoa Nguyen

单行答案:flatMap 有助于将 Collection<Collection<T>> 扁平化为 Collection<T>。同样,它也会将 Optional<Optional<T>> 扁平化为 Optional<T>

https://i.stack.imgur.com/7e1tY.jpg

如您所见,仅使用 map()

中间类型是 Stream>

返回类型是 List>

并使用 flatMap()

中间类型是 Stream

返回类型是 List

这是下面使用的代码的测试结果:

-------- Without flatMap() -------------------------------
     collect() returns: [[Laptop, Phone], [Mouse, Keyboard]]

-------- With flatMap() ----------------------------------
     collect() returns: [Laptop, Phone, Mouse, Keyboard]

使用的代码:

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

public class Parcel {
  String name;
  List<String> items;

  public Parcel(String name, String... items) {
    this.name = name;
    this.items = Arrays.asList(items);
  }

  public List<String> getItems() {
    return items;
  }

  public static void main(String[] args) {
    Parcel amazon = new Parcel("amazon", "Laptop", "Phone");
    Parcel ebay = new Parcel("ebay", "Mouse", "Keyboard");
    List<Parcel> parcels = Arrays.asList(amazon, ebay);

    System.out.println("-------- Without flatMap() ---------------------------");
    List<List<String>> mapReturn = parcels.stream()
      .map(Parcel::getItems)
      .collect(Collectors.toList());
    System.out.println("\t collect() returns: " + mapReturn);

    System.out.println("\n-------- With flatMap() ------------------------------");
    List<String> flatMapReturn = parcels.stream()
      .map(Parcel::getItems)
      .flatMap(Collection::stream)
      .collect(Collectors.toList());
    System.out.println("\t collect() returns: " + flatMapReturn);
  }
}

非常清晰的例子......,用你的例子理解这个概念不会超过几秒钟......
很好的解释。非常感谢简单和最佳的解释
感谢您提到 Optional> 到 Optional。否则,它会被我回答!
e
epox

.map 用于 A -> B 映射

Stream.of("dog", "cat")              // stream of 2 Strings
    .map(s -> s.length())            // stream of 2 Integers: [3, 3]

它将任何项目 A 转换为任何项目 BJavadoc

.flatMap 用于 A -> Stream< B> 连接

Stream.of("dog", "cat")             // stream of 2 Strings
    .flatMapToInt(s -> s.chars())   // stream of 6 ints:      [d, o, g, c, a, t]

它 --1 将任何项目 A 转换为 Stream< B>,然后 --2 将所有流连接成一个(平面)流。 Javadoc

注 1:虽然后一个示例扁平化为原始流 (IntStream) 而不是对象流 (Stream),但它仍然说明了 .flatMap 的思想。

注 2:尽管有名称,但 String.chars() 方法返回整数。所以实际的集合将是: [100, 111, 103, 99, 97, 116] ,其中 100'd' 的代码,111'o' 的代码等。同样,为了说明目的,它表示为 [d, o, g , 猫]。


我比其他人更喜欢这个答案。
只需替换为 toCharArray 并使您的帖子更短。
P
Philipp

您传递给 stream.map 的函数必须返回一个对象。这意味着输入流中的每个对象都会在输出流中产生一个对象。

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


为什么要“为每个输入对象返回任意数量的对象(包括无)”?
@DerekMahar 这会有很多用例。例如,假设您的组织中有一个 Department 流。每个部门都有 0 到 n 个Employee。您需要的是所有员工的流。所以你会怎么做?您编写了一个 flatMap 方法,该方法接受一个部门并返回其员工流。
Philipp,您的示例是否说明了使用 flatMap 的主要原因?我怀疑这可能是偶然的,并没有说明关键用例或 flatMap 存在的原因。 (下面继续……)
阅读 dzone.com/articles/understanding-flatmap 后,我认为 flatMap 背后的主要动机是适应使用 map 时可能出现的错误。您如何处理原始集合中的一个或多个项目无法映射到输出项目的情况?通过为每个输入对象引入一个中间集(例如 OptionalStream),flatMap 允许您排除“无效”输入对象(或本着 stackoverflow.com/a/52248643/107158 的精神所谓的“坏苹果” ) 从最后一组。
@DerekMahar 是的,每个输入对象可能会或可能不会返回输出对象的情况是平面地图的另一个很好的用例。
B
Bachiri Taoufiq Abderrahman

对于 Map 我们有一个元素列表和一个 (function,action) f 所以:

[a,b,c] f(x) => [f(a),f(b),f(c)]

对于平面地图,我们有一个元素列表,我们有一个 (function,action) f,我们希望结果是扁平的:

[[a,b],[c,d,e]] f(x) =>[f(a),f(b),f(c),f(d),f(e)]

G
Grzegorz Piwowarek

我有一种感觉,这里的大多数答案都使简单的问题过于复杂。如果您已经了解 map 的工作原理,那应该很容易掌握。

在某些情况下,使用 map() 时我们可能会得到不需要的嵌套结构,flatMap() 方法旨在通过避免包装来克服这个问题。

例子:

1

List<List<Integer>> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
  .collect(Collectors.toList());

我们可以使用 flatMap 避免嵌套列表:

List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
  .flatMap(i -> i.stream())
  .collect(Collectors.toList());

2

Optional<Optional<String>> result = Optional.of(42)
      .map(id -> findById(id));

Optional<String> result = Optional.of(42)
      .flatMap(id -> findById(id));

在哪里:

private Optional<String> findById(Integer id)

抱歉,第 1 点的第二个片段没有编译而不是 List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(i -> i) .collect(Collectors.toList()); 。它应该是 Stream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(List::stream) .collect(Collectors.toList());
@arthur 我想我在这里使用了 Vavr 的 Stream 和 List - 但我同意它可能会有点混乱 - 我会将其更改为标准 Java
@GrzegorzPiwowarek how about this simple explanation
c
codeforester

地图()和平面地图()

地图()

只需要一个 Function 一个 lambda 参数,其中 T 是元素,R 是使用 T 构建的返回元素。最后,我们将拥有一个带有 R 类型对象的 Stream。一个简单的例子可以是:

Stream
  .of(1,2,3,4,5)
  .map(myInt -> "preFix_"+myInt)
  .forEach(System.out::println);

它只需要类型 Integer 的元素 1 到 5,使用每个元素从类型 String 构建一个新元素,其值为 "prefix_"+integer_value 并将其打印出来。

平面图()

知道 flatMap() 采用函数 F<T, R> 是很有用的,其中

是一种可以从中构建 Stream 的类型。它可以是列表 (T.stream())、数组 (Arrays.stream(someArray)) 等。Stream 可以包含/或形成的任何内容。在下面的示例中,每个开发人员都有多种语言,因此 dev. Languages 是一个列表,将使用 lambda 参数。

是将使用 T 构建的结果流。知道我们有许多 T 实例,我们自然会有许多来自 R 的流。所有这些来自类型 R 的流现在将组合成来自类型 R 的单个“平面”流.

例子

Bachiri Taoufiq [在此处查看其答案] 1 的示例简单易懂。为了清楚起见,假设我们有一个开发团队:

dev_team = {dev_1,dev_2,dev_3}

,每个开发人员都了解多种语言:

dev_1 = {lang_a,lang_b,lang_c},
dev_2 = {lang_d},
dev_3 = {lang_e,lang_f}

在 dev_team 上应用 Stream.map() 以获取每个开发人员的语言:

dev_team.map(dev -> dev.getLanguages())

会给你这个结构:

{ 
  {lang_a,lang_b,lang_c},
  {lang_d},
  {lang_e,lang_f}
}

这基本上是一个List<List<Languages>> /Object[Languages[]]。不是很漂亮,也不是Java8!

使用 Stream.flatMap(),您可以将事物“展平”,因为它采用上述结构
并将其转换为 {lang_a, lang_b, lang_c, lang_d, lang_e, lang_f},它基本上可以用作 List<Languages>/Language[]/etc...

所以最后,你的代码会像这样更有意义:

dev_team
   .stream()    /* {dev_1,dev_2,dev_3} */
   .map(dev -> dev.getLanguages()) /* {{lang_a,...,lang_c},{lang_d}{lang_e,lang_f}}} */
   .flatMap(languages ->  languages.stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
   .doWhateverWithYourNewStreamHere();

或者简单地说:

dev_team
       .stream()    /* {dev_1,dev_2,dev_3} */
       .flatMap(dev -> dev.getLanguages().stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
       .doWhateverWithYourNewStreamHere();

何时使用 map() 和使用 flatMap():

当流中 T 类型的每个元素都应该映射/转换为 R 类型的单个元素时,请使用 map()。结果是类型(1 个开始元素 -> 1 个结束元素)和新元素流的映射返回类型 R。

当流中 T 类型的每个元素都应该映射/转换为 R 类型元素的集合时,请使用 flatMap()。结果是类型的映射(1 个开始元素 -> n 个结束元素)。然后将这些集合合并(或展平)到 R 类型的新元素流。这对于表示嵌套循环很有用。

Java 8 之前:

List<Foo> myFoos = new ArrayList<Foo>();
    for(Foo foo: myFoos){
        for(Bar bar:  foo.getMyBars()){
            System.out.println(bar.getMyName());
        }
    }

发布 Java 8

myFoos
    .stream()
    .flatMap(foo -> foo.getMyBars().stream())
    .forEach(bar -> System.out.println(bar.getMyName()));

R
Rusty Core

Oracle 关于 Optional 的文章强调了 map 和 flatmap 之间的这种区别:

String version = computer.map(Computer::getSoundcard)
                  .map(Soundcard::getUSB)
                  .map(USB::getVersion)
                  .orElse("UNKNOWN");

不幸的是,这段代码无法编译。为什么?变量 computer 是 Optional 类型,因此调用 map 方法是完全正确的。但是,getSoundcard() 返回一个 Optional 类型的对象。这意味着映射操作的结果是 Optional> 类型的对象。结果,对 getUSB() 的调用无效,因为最外层的 Optional 包含另一个 Optional 作为其值,当然它不支持 getUSB() 方法。对于流,flatMap 方法将函数作为参数,该函数返回另一个流。此函数应用于流的每个元素,这将导致流的流。但是,flatMap 具有将每个生成的流替换为该流的内容的效果。换句话说,该函数生成的所有单独的流都被合并或“扁平化”为一个流。我们在这里想要的是类似的东西,但我们想要将两级 Optional “扁平化”为一个。 Optional 还支持 flatMap 方法。其目的是将转换函数应用于 Optional 的值(就像 map 操作一样),然后将生成的两级 Optional 扁平化为一个。因此,为了使我们的代码正确,我们需要使用 flatMap 将其重写如下:

String version = computer.flatMap(Computer::getSoundcard)
                   .flatMap(Soundcard::getUSB)
                   .map(USB::getVersion)
                   .orElse("UNKNOWN");

第一个 flatMap 确保返回 Optional 而不是 Optional>,第二个 flatMap 实现相同的目的以返回 Optional。请注意,第三个调用只需要是一个 map(),因为 getVersion() 返回一个 String 而不是 Optional 对象。

http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html


问题是关于 Stream.map 和 Stream.flatMap 而不是关于 Optional.map 和 Optional.flatMap
但这对我理解可选和平面图的问题有很大帮助,非常感谢!
@djames,这是一个完全有效的答案,从“使用流,flatMap 方法将函数作为参数......”段落开始阅读:)
我认为这是对此处其他一些答案的非常有用的补充。
如果 soundCard 为 null,flatMap() 版本也会抛出 nullpointerexception。那么 Optional 承诺的好处在哪里呢?
E
Eugene

我不太确定我应该回答这个问题,但每次我面对不理解这一点的人时,我都会使用相同的例子。

想象你有一个苹果。例如,map 正在将该苹果转换为 apple-juice一对一 映射。

取同一个苹果,只从中取出种子,这就是 flatMap 所做的,或者 一对多,一个苹果作为输入,许多种子作为输出。


对于 flatMap 案例,您是否首先将每个苹果的种子分别装在单独的袋子中,每个苹果一个袋子,然后再将所有袋子倒入一个袋子中?
@DerekMahar 在 java-10 之前它曾经是一个穷人,这意味着 flatmap 并不是真的懒惰,但自从 java-10 之后它是懒惰的
@Eugene请解释一个更懒惰的概念,你试图解释的我不清楚。我明白derkerMahar在评论中解释的是java10之前发生了什么?
@JAVA 只需搜索 flatMap + lazy,我敢打赌会有一些答案。
@Eugene - flatMap 将一次取一个苹果,提取其种子并将其放入袋/集合/数组中,直到提取所有苹果种子。正确的 ?流是这样工作的吗?
Y
Yoon5oo

Map:- 此方法将一个函数作为参数并返回一个新流,该流包含通过将传递的函数应用于流的所有元素而生成的结果。

让我们想象一下,我有一个整数值列表( 1,2,3,4,5 )和一个函数接口,其逻辑是传递整数的平方。 ( e -> e * e )。

List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);

List<Integer> newList = intList.stream().map( e -> e * e ).collect(Collectors.toList());

System.out.println(newList);

输出:-

[1, 4, 9, 16, 25]

如您所见,输出是一个新流,其值是输入流值的平方。

[1, 2, 3, 4, 5] -> apply e -> e * e -> [ 1*1, 2*2, 3*3, 4*4, 5*5 ] -> [1, 4, 9, 16, 25 ]

http://codedestine.com/java-8-stream-map-method/

FlatMap :- 该方法接受一个函数作为参数,该函数接受一个参数 T 作为输入参数,并返回一个参数流 R 作为返回值。当此函数应用于此流的每个元素时,它会生成一个新值流。然后将每个元素生成的这些新流的所有元素复制到一个新流中,这将是该方法的返回值。

让我们想象一下,我有一个学生对象列表,每个学生都可以选择多个科目。

List<Student> studentList = new ArrayList<Student>();

  studentList.add(new Student("Robert","5st grade", Arrays.asList(new String[]{"history","math","geography"})));
  studentList.add(new Student("Martin","8st grade", Arrays.asList(new String[]{"economics","biology"})));
  studentList.add(new Student("Robert","9st grade", Arrays.asList(new String[]{"science","math"})));

  Set<Student> courses = studentList.stream().flatMap( e -> e.getCourse().stream()).collect(Collectors.toSet());

  System.out.println(courses);

输出:-

[economics, biology, geography, science, history, math]

如您所见,输出是一个新流,其值是输入流的每个元素返回的流的所有元素的集合。

[ S1 , S2 , S3 ] -> [ {"history","math","geography"}, {"economics","biology"}, {"science","math"} ] -> 选择独特的科目 - > [经济学、生物、地理、科学、历史、数学]

http://codedestine.com/java-8-stream-flatmap-method/


如果您提供代码而不是仅仅提供文档链接,可能会有所作为
W
WesternGun

如果您将 map() 视为一个迭代(一级 for 循环),则 flatmap() 是一个两级迭代(如嵌套的 for 循环)。 (输入每个迭代元素 foo,然后执行 foo.getBarList() 并再次迭代该 barList

map():取一个流,对每个元素做一些事情,收集每个过程的单个结果,输出另一个流。 “做某事”的定义是隐含的。如果任何元素的处理导致 null,则 null 用于组成最终流。因此,结果流中的元素数量将等于输入流的数量。

flatmap():获取 elements/streams 的流和一个函数(显式定义),将函数应用于每个流的每个元素,并将所有中间结果流收集为更大的流(“扁平化”)。如果任何元素的处理导致null,则将空流提供给“展平”的最后一步。如果输入是多个流,则结果流中的元素数是所有输入中所有参与元素的总和。


H
Harry Coder

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

如果您有一个平面元素列表,请使用 map:[0, 1, 2, 3, 4, 5]

如果您有一个元素列表列表,请使用 flatMap:[[1, 3, 5], [2, 4, 6]]。这意味着,您的列表需要展平,然后才能将地图操作应用于每个元素


M
Melad Basilius

简单的回答。

map 操作可以产生 StreamStream.EX Stream<Stream<Integer>>

flatMap 操作只会产生 Stream 的东西。前 Stream<Integer>


N
Niraj Chauhan

这对于初学者来说是非常混乱的。基本区别是 map 为列表中的每个条目发出一个项目,而 flatMap 基本上是 map + flatten 操作。更清楚地说,当您需要多个值时使用 flatMap,例如,当您期望循环返回数组时,flatMap 在这种情况下将非常有用。

我已经写了一篇关于此的博客,您可以查看here


H
Hulk

流操作 flatMapmap 接受一个函数作为输入。

flatMap 期望函数为流的每个元素返回一个新流,并返回一个流,该流组合了函数为每个元素返回的流的所有元素。换句话说,使用 flatMap,对于来自源的每个元素,函数将创建多个元素。 http://www.zoftino.com/java-stream-examples#flatmap-operation

map 期望函数返回一个转换后的值并返回一个包含转换后元素的新流。换句话说,使用 map,对于来自源的每个元素,函数将创建一个转换后的元素。 http://www.zoftino.com/java-stream-examples#map-operation


S
S.K.

flatMap() 还利用了流的部分惰性求值。它将读取第一个流,并且仅在需要时才会转到下一个流。此处详细解释了该行为:Is flatMap guaranteed to be lazy?


A
Arsenius

如果您熟悉的话,也可以用 C# 进行很好的类比。基本上 C# Select 类似于 java map 和 C# SelectMany java flatMap。同样适用于 Kotlin 的集合。