Java 8에서 map과 flatMap 메소드의 차이점은 무엇입니까?
Java 8에서 Stream.map
및 Stream.flatMap
메소드 의 차이점은 무엇 입니까?
map
및 둘 다 flatMap
a에 적용 할 수 있으며 Stream<T>
둘 다 Stream<R>
. 차이점은 map
연산은 각 입력 값에 대해 하나의 출력 값을 flatMap
생성하는 반면 연산은 각 입력 값에 대해 임의의 숫자 (0 개 이상의) 값을 생성한다는 것입니다.
이는 각 작업에 대한 인수에 반영됩니다.
이 map
작업은 Function
입력 스트림의 각 값에 대해 호출되고 하나의 결과 값을 생성하는를 가져와 출력 스트림으로 보냅니다.
이 flatMap
작업은 개념적으로 하나의 값을 사용하고 임의의 값을 생성하려는 함수를 사용합니다. 그러나 Java에서는 메서드가 0 또는 하나의 값만 반환 할 수 있으므로 임의의 수의 값을 반환하는 것이 번거 롭습니다. 매퍼 함수 flatMap
가 값 을 취하고 배열 또는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] ]
"두 단계"가 있는 구조를 고려하십시오 . 이것을 평평하게한다는 것은 그것을 "한 레벨"구조로 변형시키는 것을 의미합니다 : [ 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);
}
첫 번째 예에서 특별한 것은 없습니다. a 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을 다른 것으로 (Integer Stream) 평면화해야합니다.
flatMap이 제거되면 다음 오류가 반환됩니다. + 연산자는 인수 유형 목록, int에 대해 정의되지 않았습니다.
정수 목록에 + 1을 적용 할 수 없습니다!
명확한 아이디어를 얻으려면 게시물을 완전히 검토하십시오.
map vs flatMap :
목록에서 각 단어의 길이를 반환하려면 아래와 같이합니다.
아래에 주어진 짧은 버전
아래에 주어진 두 가지 목록을 수집 할 때
평면지도 없이 => [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 :
여러분은 플랫 맵을 사용하여이 문제를 해결할 수 있다고 생각할 수 있습니다. 좋습니다. map 과 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)]
여기에있는 대부분의 답변이 단순한 문제를 지나치게 복잡하게 만든다고 생각합니다. 이해 map
하기 쉬운 일이 무엇인지 이미 이해하고 있다면 .
를 사용할 때 원하지 않는 중첩 구조로 끝날 수있는 경우가 있습니다 map()
.이 flatMap()
메서드는 래핑을 피하여이를 극복하도록 설계되었습니다.
예 :
1
List<List<Integer>> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
.collect(Collectors.toList());
다음을 사용하여 중첩 된 목록을 피할 수 있습니다 flatMap
.
List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
.flatMap(i -> i.stream())
.collect(Collectors.toList());
2
Optional<Optional<String>> result = Optional.of(42)
.map(id -> findById(id));
Optional<String> result = Optional.of(42)
.flatMap(id -> findById(id));
어디:
private Optional<String> findById(Integer id)
Optional에 대한 Oracle의 기사는 map과 flatmap의 차이점을 강조합니다.
String version = computer.map(Computer::getSoundcard)
.map(Soundcard::getUSB)
.map(USB::getVersion)
.orElse("UNKNOWN");
불행히도이 코드는 컴파일되지 않습니다. 왜? 변수 컴퓨터는 유형
Optional<Computer>
이므로 map 메서드를 호출하는 것이 완벽합니다. 그러나 getSoundcard ()는 Optional 유형의 객체를 반환합니다. 이는지도 작업의 결과가 유형의 객체임을 의미합니다Optional<Optional<Soundcard>>
. 결과적으로 가장 바깥 쪽의 Optional에 다른 Optional이 포함되어 있으므로 getUSB () 호출은 유효하지 않습니다. 물론 getUSB () 메서드를 지원하지 않습니다.스트림을 사용하는 경우 flatMap 메서드는 함수를 인수로 사용하여 다른 스트림을 반환합니다. 이 함수는 스트림의 각 요소에 적용되며 결과적으로 스트림 스트림이 생성됩니다. 그러나 flatMap은 생성 된 각 스트림을 해당 스트림의 내용으로 대체하는 효과가 있습니다. 즉, 함수에 의해 생성 된 모든 개별 스트림이 하나의 단일 스트림으로 합쳐 지거나 "평탄화"됩니다. 여기서 우리가 원하는 것은 비슷한 것이지만 , 2 단계 Optional을 하나로 "평탄화"하고 싶습니다 .
선택 사항은 flatMap 메서드도 지원합니다. 그 목적은 (지도 작업과 마찬가지로) Optional의 값에 변환 함수를 적용한 다음 결과로 생성되는 2 단계 Optional을 하나의 것으로 병합하는 것 입니다.
따라서 코드를 올바르게 작성하려면 flatMap을 사용하여 다음과 같이 다시 작성해야합니다.
String version = computer.flatMap(Computer::getSoundcard)
.flatMap(Soundcard::getUSB)
.map(USB::getVersion)
.orElse("UNKNOWN");
첫 번째 flatMap은이 대신를
Optional<Soundcard>
반환Optional<Optional<Soundcard>>
하도록하고 두 번째 flatMap은Optional<USB>
. getVersion ()은 Optional 객체가 아닌 String을 반환하기 때문에 세 번째 호출은 map ()이면됩니다.
http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html
이 질문에 대답해야할지 잘 모르겠지만, 이것을 이해하지 못하는 사람을 만날 때마다 같은 예를 사용합니다.
사과가 있다고 상상해보십시오. A map
는 그 사과를 apple-juice
예를 들어 일대일 매핑으로 변환하고 있습니다.
똑같은 사과를 가져다가 씨앗 만 꺼내십시오. 즉 flatMap
, 일대 다 , 하나의 사과를 입력으로, 많은 씨앗을 출력으로 가져옵니다 .
Map :-이 메서드는 하나의 함수를 인수로 사용하고 전달 된 함수를 스트림의 모든 요소에 적용하여 생성 된 결과로 구성된 새 스트림을 반환합니다.
정수 값 목록 (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 ] -> [ {"history","math","geography"}, {"economics","biology"}, {"science","math"} ] -> take unique subjects -> [economics, biology, geography, science, history, math]
http://codedestine.com/java-8-stream-flatmap-method/
map() and flatMap()
map()
Just takes a Function a lambda param where T is element and R the return element built using T. At the end we'll have a Stream with objects of Type R. A simple example can be:
Stream
.of(1,2,3,4,5)
.map(myInt -> "preFix_"+myInt)
.forEach(System.out::println);
It simply takes elements 1 to 5 of Type Integer
, uses each element to build a new element from type String
with value "prefix_"+integer_value
and prints it out.
flatMap()
It is useful to know that flapMap() takes a function F<T, R>
where
T is a type from which a Stream can be built from/with. It can be a List (T.stream()), an array (Arrays.stream(someArray)), etc.. anything that from which a Stream can be with/or form. in the example below each dev has many languages, so dev. Languages is a List and will use a lambda parameter.
R is the resulting Stream that will be built using T. Knowing that we have many instances of T, we will naturally have many Streams from R. All these Streams from Type R will now be combined into one single 'flat' Stream from Type R.
Example
The examples of Bachiri Taoufiq see its answer here are simple and easy to understanding. Just for clarity, let just say we have a team of developers:
dev_team = {dev_1,dev_2,dev_3}
, with each developer knowing many languages:
dev_1 = {lang_a,lang_b,lang_c},
dev_2 = {lang_d},
dev_2 = {lang_e,lang_f}
Applying Stream.map() on dev_team to get the languages of each dev:
dev_team.map(dev -> dev.getLanguages())
will give you this structure:
{
{lang_a,lang_b,lang_c},
{lang_d},
{lang_e,lang_f}
}
which is basically a List<List<Languages>> /Object[Languages[]]
. Not so very pretty, nor Java8-like!!
with Stream.flatMap()
you can 'flatten' things out as it takes the above structure
and turns it into {lang_a, lang_b, lang_c, lang_d, lang_e, lang_f}
, which can basically used as List<Languages>/Language[]/ect
...
so the end your code would make more sense like this:
dev_team
.stream() /* {dev_1,dev_2,dev_3} */
.map(dev -> dev.getLanguages()) /* {{lang_a,...,lang_c},{lang_d}{lang_e,lang_f}}} */
.flatMap(languages -> languages.stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
.doWhateverWithYourNewStreamHere();
or simply:
dev_team
.stream() /* {dev_1,dev_2,dev_3} */
.flatMap(dev -> dev.getLanguages().stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
.doWhateverWithYourNewStreamHere();
When to use map() and use flatMap():
Use
map()
when each element of type T from your stream is supposed to be mapped/transformed to a single element of type R. The result is a mapping of type (1 start element -> 1 end element) and new stream of elements of type R is returned.Use
flatMap()
when each element of type T from your stream is supposed to mapped/transformed to a Collections of elements of type R. The result is a mapping of type (1 start element -> n end elements). These Collections are then merged (or flattened) to a new stream of elements of type R. This is useful for example to represent nested loops.
Pre Java 8:
List<Foo> myFoos = new ArrayList<Foo>();
for(Foo foo: myFoos){
for(Bar bar: foo.getMyBars()){
System.out.println(bar.getMyName());
}
}
Post Java 8
myFoos
.stream()
.flat(foo -> foo.getMyBars().stream())
.forEach(bar -> System.out.println(bar.getMyName()));
Also good analogy can be with C# if you familiar with. Basically C# Select
similar to java map
and C# SelectMany
java flatMap
. Same applies to Kotlin for collections.
This is very confusing for beginners. The basic difference is map
emits one item for each entry in the list and flatMap
is basically a map
+ flatten
operation. To be more clear, use flatMap when you require more than one value, eg when you are expecting a loop to return arrays, flatMap will be really helpful in this case.
I have written a blog about this, you can check it out here.
Simple answer.
The map
operation can produce a Stream
of Stream
.EX Stream<Stream<Integer>>
flatMap
operation will only produce Stream
of something. EX Stream<Integer>
Stream operations flatMap
and map
accept a function as input.
flatMap
expects the function to return a new stream for each element of the stream and returns a stream which combines all the elements of the streams returned by the function for each element. In other words, with flatMap
, for each element from the source, multiple elements will be created by the function. http://www.zoftino.com/java-stream-examples#flatmap-operation
map
expects the function to return a transformed value and returns a new stream containing the transformed elements. In other words, with map
, for each element from the source, one transformed element will be created by the function. http://www.zoftino.com/java-stream-examples#map-operation
flatMap()
also takes advantage of partial lazy evaluation of streams. It will read the fist stream and only when required, will go to the next stream. The behaviour is explained in detail here: Is flatMap guaranteed to be lazy?
map() in Java 8
a stream consisting of the results of applying the given function to the elements of this stream. Map takes an input which describes how the value needs to be transformed into. Suppose we want to get the age of the Student whose name is Saurabh, till now we have only retrieved the complete object from the stream but how do we do this ? We can use map() to transform the Student Stream into the age stream as below.
int age = students.stream()
.filter(student -> SAURABH.equals(student.getName()))
.map(Student::getAge)
.findAny()
.orElse(0);
System.out.printf("*** Age of %s is %d\n",SAURABH, age);
Now lets try to get all the names of the students with the help of collect()
Set<String> names = students.stream()
.map(Student::getName) // this will convert the Student Stream into String Stream by
// applying the getName()
.collect(Collectors.toSet());
System.out.printf("*** All the names from the list is %s\n",names);
map() vs flatMap()
Suppose we want to get all the courses available in students list then we can write the code as below:
Set<String> courses = students.stream()
.map(Student::getCourses)
.collect(Collectors.toSet())
**Here we will get a compilation error as below
Type mismatch: cannot convert from Set to Set To resolve this issue we use flatMap()**
flatMap() in Java 8
It returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element. flatMap will transform the stream of stream into simple stream. In below example we are using the flatMap to convert the Array of Stream into the String stream.
Set<String> courses = students.stream()
.map(Student::getCourses)
.flatMap(Arrays::stream)
.collect(Collectors.toSet());
For more information you can refer to below links :
https://onlyfullstack.blogspot.com/2018/12/map-vs-flatmap-in-java-8.html
'Nice programing' 카테고리의 다른 글
SQL Server 2008 열 이름 바꾸기 (0) | 2020.10.02 |
---|---|
프로그래밍 방식으로 ImageView 너비와 높이를 설정 하시겠습니까? (0) | 2020.10.02 |
Django 뷰에서 2 개 이상의 쿼리 셋을 결합하는 방법은 무엇입니까? (0) | 2020.10.02 |
MongoDB를 통해 CouchDB를 사용하거나 그 반대의 경우 (0) | 2020.10.02 |
JavaScript 함수를 매개 변수로 전달 (0) | 2020.10.02 |