Stream API. 자바로 개발을 하면 자주 쓰게 되는 API 중 하나입니다. 그리고 map과 flatMap은 stream api와 자주 쓰이는 걸 볼 수 있습니다. 이 두 친구들은 똑같은 type을 return 하지만 어느정도 차이가 있는데요.어떻게 다른지 이 포스트에서 설명해드리겠습니다.
Map
공식 Documentation의 정의는 "map은 주어진 함수를 통해 만들어진 결과들을 Stream의 형식으로 return한다는 말인데요." 쉽게 말하자면 Argument로 받은 Function을 stream이 가지고 있는 element의 값을 바꿔주는 겁니다.
Example
List<Integer> mapPractice = List.of(1,2,3,4,5);
List<Integer> afterMap = mapPractice.stream()
.map(element -> element * 2)
.toList();
mapPractice는 Integer을 가진 기본적인 List입니다. 그리고 afterMap은 map을 사용한 후의 List인데요. Stream API에 속해있듯이 stream을 선언하고 map에게 mapPractice의 element를 2배씩 해달라는 function을 보냅니다. 그러면 afterMap의 element는 아래와 같이 나오게 됩니다.
List.of(2,4,6,8,10)
다른 예를 보자면 아래와 같이도 할 수 있습니다.
List<Integer> mapPractice = List.of(1,2,3,4,5);
List<String> afterMap = mapPractice.stream()
.map(Object::toString)
.toList();
Integer였던 mapPractice의 element들을 String으로 바꿔주는 방식인데요. 이것 또한 map을 통해 각각의 element들을 String으로 바꿔서 String 타입의 List를 가지고 있는 afterMap을 만들어줄 수 있습니다.
flatMap
flatMap의 공식 Documentation에 따르면 각각 element에 사용된 maaping 함수를 적용해서 만들어진 mapped stream의 콘텐츠가 원래 있던 element들을 대체한 뒤 stream을 return한다고 합니다. 역시나 번역체는 이해가 잘 안갑니다. 그러면 여기서 주목해야 할것은 "mapped stream"입니다.
flatMap은 애초에 Function<? super T, ? extends Stream <? extends R>>을 argument로 받는데요. 이미 Mapped stream이 된 콘텐츠를 argument로 받아서 function을 통해 변화를 주는 방법인데요. 풀어서 얘기하자면 Mapped된 elements들을 function을 통해 변화를 주고 2차원 배열에서 1차원으로 바꿔줄 수 있습니다.
Example
("cat", "dog", "deer")을 가지고 있는 list를 ("c", "a", "t", "d", "o", "g", "d", "e", "e", "r")로 바꾸고 싶다고 해봅시다. map을 사용하면 어떻게 될까요?
List<String> animalsList = List.of("cat", "dog", "deer");
List<String[]> mappedAnimals = animalsList.stream()
.map(animal -> animal.split(""))
.collect(Collectors.toList());
이 결과 값은 {("c", "a", "t), ("d", "o", "g"), ("d", "e", "e", "r")}이 나옵니다. 왜냐면 map은 function을 통해서 기존 list의 element를 바꿔줄 뿐 mapping을 하지는 못하기 떄문이죠.
만약에 flatmap을 사용하면 어떻게 될까요?
List<String> animalsList = List.of("cat", "dog", "deer");
List<String> mappedAnimals = animalsList.stream()
.map(animals -> animals.split(""))
.flatMap(Arrays::stream)
.collect(Collectors.toList());
map을 통해 만들어진 String[] 값을 mapping을 한 번 또 해주면서 List<String>으로 바꿔줍니다. List<String[]>을 List<String>으로 평평하게 바꿔준거죠. 고로 이 수식의 결과 값은 ("c", "a", "t", "d", "o", "g", "d", "e", "e", "r")가 나오게 됩니다.
Conclusion
GeeksForGeeks에 따르면 map과 flatmap의 차이점은 아래와 같이 정리되어 있습니다.
map() | flatMap() |
The function passed to map() operation retruns a single value for a single input. | The function you pass to flatMap() operation returns an arbitrary number of values as the output. |
One-to-one mapping occurs in map(). | One-to-many mapping occurs in flatMap() |
Only perform the mapping. | Perform mapping as well as flattening. |
Produce a stream of value. | Produce a stream of stream value. |
map() is used only for transformation | flatMap() is used both for transformation and mapping |
map은 기존의 list안에 있는 element를 바꿔주기만 하고, flatMap은 바꿔주기도 하면서 mapping을 해주기도 합니다. 그래서 간혹 한 번에 똑같은 객체들을 iterate하고, 그 객체가 포함하고 있는 list들에 변화를 줘야할 때 map과 flatMap을 적절히 사용해주면 for loop 여러번을 사용하는 대신 훨씬 더 깔끔한 코드를 만들어 줄겁니다.
References
1.https://medium.com/javarevisited/map-vs-flatmap-d027df9b9b56
2.https://www.geeksforgeeks.org/difference-between-map-and-flatmap-in-java-stream/
3.https://blog.naver.com/yosong_is_yosong/223108604534
'Java' 카테고리의 다른 글
[Data Structure] List.of() (0) | 2023.09.10 |
---|---|
오버라이딩(Overriding) vs. 오버로딩(Overloading) (0) | 2022.01.13 |
객체(Object)란 무엇일까요? (0) | 2020.04.25 |