The Difference Between map() and flatMap() – map()和flatMap()的区别

最后修改: 2017年 6月 2日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

map() and flatMap() APIs stem from functional languages. In Java 8, we can find them in Optional, Stream and in CompletableFuture (although under a slightly different name).

map()flatMap() API源于函数式语言。在Java 8中,我们可以在OptionalStreamCompletableFuture中找到它们(尽管名称略有不同)。

Streams represent a sequence of objects whereas optionals are classes that represent a value that can be present or absent. Among other aggregate operations, we have the map() and flatMap() methods.

Streams代表一个对象序列,而optionals是代表一个可以存在或不存在的值的类。在其他聚合操作中,我们有map()flatMap()方法。

Despite the fact that both have the same return types, they are quite different. Let’s explain these differences by analyzing some examples of streams and optionals.

尽管两者有相同的返回类型,但它们有很大的不同。让我们通过分析流和选项的一些例子来解释这些区别。

2. Map and Flatmap in Optionals

2.选项中的地图和平面图

The map() method works well with Optional — if the function returns the exact type we need:

map()方法与Optional配合得很好–如果函数返回我们需要的确切类型。

Optional<String> s = Optional.of("test");
assertEquals(Optional.of("TEST"), s.map(String::toUpperCase));

However, in more complex cases we might be given a function that returns an Optional too. In such cases using map() would lead to a nested structure, as the map() implementation does an additional wrapping internally.

然而,在更复杂的情况下,我们可能会被赋予一个也会返回Optional的函数。在这种情况下,使用map()将导致一个嵌套结构,因为map()的实现在内部做了一个额外的包装。

Let’s see another example to get a better understanding of this situation:

让我们看看另一个例子,以更好地了解这种情况。

assertEquals(Optional.of(Optional.of("STRING")), 
  Optional
  .of("string")
  .map(s -> Optional.of("STRING")));

As we can see, we end up with the nested structure Optional<Optional<String>>. Although it works, it’s pretty cumbersome to use and does not provide any additional null safety, so it is better to keep a flat structure.

我们可以看到,我们最终得到了嵌套结构Optional<Optional<String>>。虽然它可以工作,但使用起来相当麻烦,而且没有提供任何额外的空值安全,所以最好是保持一个扁平结构。

That’s exactly what flatMap() helps us to do:

这正是flatMap()帮助我们做到的。

assertEquals(Optional.of("STRING"), Optional
  .of("string")
  .flatMap(s -> Optional.of("STRING")));

3. Map and Flatmap in Streams

3.流中的地图和平面图

Both methods work similarly for Optional.

这两种方法对Optional的作用类似。

The map() method wraps the underlying sequence in a Stream instance, whereas the flatMap() method allows avoiding nested Stream<Stream<R>> structure.

map() 方法将底层序列包裹在一个Stream实例中,而flatMap() 方法允许避免嵌套Stream<Stream<R>>结构。

Here, map() produces a Stream consisting of the results of applying the toUpperCase() method to the elements of the input Stream:

这里,map()产生一个Stream,由对输入Stream中的元素应用toUpperCase()方法的结果组成。

List<String> myList = Stream.of("a", "b")
  .map(String::toUpperCase)
  .collect(Collectors.toList());
assertEquals(asList("A", "B"), myList);

map() works pretty well in such a simple case. But what if we have something more complex, such as a list of lists as an input?

map()在这样一个简单的情况下工作得非常好。但如果我们有更复杂的东西,比如一个列表作为输入怎么办?

Let’s see how it works:

让我们看看它是如何工作的。

List<List<String>> list = Arrays.asList(
  Arrays.asList("a"),
  Arrays.asList("b"));
System.out.println(list);

This snippet prints a list of lists [[a], [b]].

这个片段打印了一个列表[[a], [b]]的列表。

Now let’s use a flatMap():

现在让我们使用一个flatMap()

System.out.println(list
  .stream()
  .flatMap(Collection::stream)
  .collect(Collectors.toList()));

The result of such a snippet will be flattened to [a, b].

这样一个片段的结果将被扁平化为[a, b]

The flatMap() method first flattens the input Stream of Streams to a Stream of Strings (for more about flattening, see this article). Thereafter, it works similarly to the map() method.

T flatMap()方法首先将输入的StreamStream改为StringsStream(关于扁平化的更多信息,请参阅本文章)。此后,它的工作方式与map()方法类似。

4. Conclusion

4.结论

Java 8 gives us the opportunity to use the map() and flatMap() methods that originally were used in functional languages.

Java 8让我们有机会使用最初在函数式语言中使用的map()flatMap()方法。

We can invoke them on Streams and Optionals. These methods help us to get mapped objects by applying the provided mapping function.

我们可以在Streams和Optionals上调用它们。这些方法帮助我们通过应用所提供的映射函数来获得映射的对象。

As always, the examples in this article are available over on GitHub.

一如既往,本文中的例子可以在GitHub上找到