[java] Java 8에서 map ()과 flatMap () 메소드의 차이점은 무엇입니까?

Java 8에서 Stream.map()Stream.flatMap()메소드 의 차이점은 무엇 입니까?



답변

모두 mapflatMapA를 적용 할 수 Stream<T>및 수익 a를 둘 Stream<R>. 차이점은 map연산이 각 입력 값에 대해 하나의 출력 값을 flatMap생성하는 반면, 연산은 각 입력 값에 대해 임의의 숫자 (0 이상) 값을 생성한다는 것입니다.

이는 각 작업에 대한 인수에 반영됩니다.

map연산은 Function입력 스트림의 각 값에 대해 호출되는 하나의 결과 값을 생성하여 출력 스트림으로 전송합니다.

flatMap동작을 개념적으로 하나 개의 값을 소비 값의 임의의 수를 생성하기 원하는 기능 걸린다. 그러나 Java에서는 메소드가 0 또는 하나의 값만 리턴 할 수 있으므로 메소드가 임의의 수의 값을 리턴하는 것은 번거 롭습니다. 매퍼 함수 flatMap가 값 을 가져 와서 배열 또는 배열을 반환하는 API를 상상할 수 있습니다 .List값으로 설정되어 출력으로 전송됩니다. 이것이 스트림 라이브러리라는 점을 감안할 때, 임의의 수의 반환 값을 나타내는 적절한 방법은 매퍼 함수 자체가 스트림을 반환하는 것입니다! 매퍼가 반환 한 스트림의 값은 스트림에서 배출되어 출력 스트림으로 전달됩니다. 매퍼 함수에 대한 각 호출에 의해 리턴 된 값의 “클럼프”는 출력 스트림에서 전혀 구별되지 않으므로 출력이 “평평하게”있다고합니다.

일반적으로 0 값을 보내려는 경우 또는 여러 값을 반환하려는 경우 flatMap반환 하는 매퍼 함수가 사용 됩니다. 물론 모든 스트림을 반환 할 수 있습니다.Stream.empty()Stream.of(a, b, c)


답변

Stream.flatMap이름에서 알 수 있듯이 a mapflat연산 의 조합입니다 . 즉, 먼저 요소에 함수를 적용한 다음 평평하게합니다. Stream.map스트림을 평탄화하지 않고 스트림에만 함수를 적용합니다.

스트림의 평탄화 가 무엇인지 이해하려면 [ [1,2,3],[4,5,6],[7,8,9] ]“2 레벨” 과 같은 구조를 고려하십시오 . 이를 평탄화한다는 것은 “한 수준”구조로 변환하는 것을 의미합니다 [ 1,2,3,4,5,6,7,8,9 ].


답변

좀 더 실용적인 관점
을 얻기 위해 두 가지 예를 제시하고 싶습니다
.

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

두 번째 예에서는 Stream of List가 전달됩니다. 정수 스트림이 아닙니다!

변환 기능을 사용해야하는 경우 (맵을 통해) 먼저 스트림을 다른 것 (정수 스트림)으로 평면화해야합니다.

flatMap이 제거되면 다음 오류가 리턴됩니다. 연산자 +는 인수 유형 List, int에 대해 정의되지 않았습니다.

정수 목록에 + 1을 적용 할 수 없습니다!


답변

명확한 아이디어를 얻으려면 게시물을 완전히 살펴보십시오.

지도 대 평면지도 :

목록에서 각 단어의 길이를 반환하려면 다음과 같이하십시오.

아래에 주어진 짧은 버전

아래에 주어진 두 목록을 수집 할 때

플랫 맵이 없는 경우 => [1,2], [1,1] => [[1,2], [1,1]] 여기에 두 개의 목록이 목록에 배치되므로 목록이 포함 된 목록이 출력됩니다.

함께 평면지도 => [1,2], [1,1] => [1,2,1,1] 다음 두 개의리스트가 평평하고 출력 요소만을 포함하는리스트 수 있도록 값만이리스트에 배치되고

기본적으로 모든 객체를 하나로 병합합니다.

자세한 내용은 다음과 같습니다.

예를 들면 : -은
목록 고려 [ “스택”, “OOOVVVER을”] 우리는 같은 목록을 반환하려고하는 [ “STACKOVER은”] 을 다시 보려면 아래처럼, 우리는 무언가를 할 것이다 처음에 (그 목록에서만 고유 한 문자를 반환를) 목록 [ “STACKOVER”] 에서 [ “STACK”, “OOOVVVER”]

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입니다. 문제.

그림 A :

여기에 이미지 설명을 입력하십시오

당신은 그 생각, 우리는 flatmap를 사용하여이 문제를 해결할 수 있습니다,
OK, 우리가 사용하여이 문제를 해결하는 방법을 볼 수 있도록 지도 하고 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());

flatMap은 스트림이 아니라 해당 스트림의 내용으로 각 배열을 매핑합니다. map (Arrays :: stream)을 사용하는 동안 생성되는 모든 개별 스트림은 단일 스트림으로 병합됩니다. 그림 B는 flatMap 메소드 사용의 효과를 보여줍니다. 그림 A의지도와 비교해보십시오.
그림 B
여기에 이미지 설명을 입력하십시오

flatMap 메소드를 사용하면 스트림의 각 값을 다른 스트림으로 교체 한 다음 생성 된 모든 스트림을 단일 스트림으로 결합 할 수 있습니다.


답변

한 줄로 답변 : flatMapa 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);
  }
}


답변

전달하는 함수 stream.map는 하나의 객체를 반환해야합니다. 즉, 입력 스트림의 각 객체는 출력 스트림에서 정확히 하나의 객체가됩니다.

전달하는 stream.flatMap함수는 각 객체에 대한 스트림 을 반환합니다. 즉, 함수는 각 입력 개체에 대해 여러 개체를 반환 할 수 있습니다 (없음 포함). 결과 스트림은 하나의 출력 스트림에 연결됩니다.


답변

Map의 경우 요소 목록과 (함수, 동작) f가 있습니다.

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

평평한 맵의 경우 요소 목록이 있고 (함수, 동작) f가 있으며 결과를 평평하게하고 싶습니다.

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