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


当前回答

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

我写了一篇关于这方面的博客,你可以在这里查看。

其他回答

地图: 该方法以一个Function作为参数,并返回一个新的流,该流由将传递的函数应用于流的所有元素所生成的结果组成。

让我们想象一下,我有一个整数值列表(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] -> [{“历史”,“数学”,“地理”},{“经济学”、“生物学”},{“科学”,“数学”}]- >采取独特的主题- - - > [经济、生物、地理、科学、历史、数学]

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

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

@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 !

一行回答: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所做的,或者一对多,一个苹果作为输入,许多种子作为输出。