Java 8에서 Stream.map()
와 Stream.flatMap()
메소드 의 차이점은 무엇 입니까?
답변
모두 map
와 flatMap
A를 적용 할 수 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 map
와 flat
연산 의 조합입니다 . 즉, 먼저 요소에 함수를 적용한 다음 평평하게합니다. 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 메소드를 사용하면 스트림의 각 값을 다른 스트림으로 교체 한 다음 생성 된 모든 스트림을 단일 스트림으로 결합 할 수 있습니다.
답변
한 줄로 답변 : flatMap
a 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)]