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


当前回答

如果您认为map()是一个迭代(一级for循环),那么flatmap()是一个两级迭代(类似于嵌套的for循环)。(输入每个迭代元素foo,然后执行foo. getbarlist()并再次迭代该barList)


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

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

其他回答

map和flatMap都可以应用于一个<T>的流,它们都返回一个<R>的流。不同之处在于map操作为每个输入值生成一个输出值,而flatMap操作为每个输入值生成任意数量(零个或多个)值。

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

map操作接受一个函数,该函数针对输入流中的每个值被调用,并产生一个结果值,该结果值被发送到输出流。

The flatMap operation takes a function that conceptually wants to consume one value and produce an arbitrary number of values. However, in Java, it's cumbersome for a method to return an arbitrary number of values, since methods can return only zero or one value. One could imagine an API where the mapper function for flatMap takes a value and returns an array or a List of values, which are then sent to the output. Given that this is the streams library, a particularly apt way to represent an arbitrary number of return values is for the mapper function itself to return a stream! The values from the stream returned by the mapper are drained from the stream and are passed to the output stream. The "clumps" of values returned by each call to the mapper function are not distinguished at all in the output stream, thus the output is said to have been "flattened."

典型的用法是flatMap的mapper函数返回Stream.empty(),如果它想发送零值,或者类似于Stream。(a, b, c)如果它想返回几个值。当然,任何流都可以返回。

一行回答:flatMap帮助将Collection<Collection<T>>平铺为Collection<T>。以同样的方式,它还将一个Optional<Optional<T>>压扁为Optional<T>。

如你所见,只使用map():

中间类型为Stream<List<Item>> 返回类型为List<List<Item>>

和flatMap():

中间类型是Stream<Item> 返回类型为List<Item>

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

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

我不太确定我是否应该回答这个问题,但每当我面对不理解这一点的人时,我就用同样的例子。

假设你有一个苹果。例如,地图是将苹果转换为苹果汁或一对一映射。

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

简单的答案。

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

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

.map用于A -> B映射

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

它将任意项A转换为任意项b


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

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

it——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, c, a, t]。