김영한님의 김영한의 실전 자바 – 고급 3편, 람다, 스트림, 함수형 프로그래밍 내용 중 일부로 실무에서 자주 사용될 법한 Stream의 flatMap이다
flatMap
- map은 각 요소를 하나의 값으로 변환하지만, flatMap은 각 요소를 스트림(또는 여러 요소)으로 변환한 뒤, 그 결과를 하나의 스트림으로 평탄화(flatten)해준다
- 군대에서 유격 때 A텐트를 치기 전에 울퉁불퉁한 바닥을 평탄화(나라시) 작업 했던 기억이 난다
리스트 안의 리스트
List<List<Integer>> outerList = List.of(
List.of(1, 2),
List.of(3, 4),
List.of(5, 6)
);
System.out.println(outerList);
[
[1, 2],
[3, 4],
[5, 6]
]
// 정확하게는 [[1, 2], [3, 4], [5, 6]] 출력
2중 for문을 통한 평탄화 작업
List<Integer> forResult = new ArrayList<>();
for (List<Integer> list : outerList) {
for (Integer i : list) {
forResult.add(i);
}
}
System.out.println(forResult);
// [1, 2, 3, 4, 5, 6]
map 사용
List<Stream<Integer>> mapResult = outerList.stream() // Stream<List<...>>
.map(list -> list.stream()) // Stream<Stream<...>>
.toList();
System.out.println(mapResult);
/*
mapResult =[
java.util.stream.ReferencePipeline$Head@7291c18f,
java.util.stream.ReferencePipeline$Head@34a245ab,
java.util.stream.ReferencePipeline$Head@7cc355be
]
*/
map을 사용하면 이중 구조(List<List<Integer>>)가 그대로 유지된다. 각 요소가 Stream으로 감싸지기 때문에 결과는 List<List<Integer>> 형태가 된다. 또한 mapResult를 직접 출력하면 실제 값이 아니라 Stream 객체의 참조값이 표시되어 [java.util.stream.ReferencePipeline$Head@7291c18f] 같은 형태가 나타난다
- outerList.stream()
List<List<Integer>>에서 바깥쪽 리스트가 스트림으로 변환되어 Stream<List<Integer>>가 된다. 즉, 스트림 안에는 3개의 List<Integer> 요소가 들어 있다 - map(list -> list.stream())
각 List<Integer>를 Stream<Integer>로 바꾸므로, 결과는 Stream<Stream<Integer>>가 된다. 즉, 바깥 스트림의 요소가 또 다른 스트림인 구조다. - toList()
바깥쪽 Stream<Stream<Integer>>에 대해 실행되므로, 최종 결과는 List<Stream<Integer>>가 된다. 내부에는 3개의 Stream<Integer>가 들어 있으며, 평탄화가 일어나지 않는다.
map은 요소를 1:1로 변환할 뿐 스트림을 병합하지 않기 때문에 평탄화 되지 않고 List<Stream<Integer>> 구조가 유지된다
flatMap 사용
List<Integer> flatMapResult = outerList.stream() // Stream<List<...>>
.flatMap(list -> list.stream()) // Stream<Integer>
.toList();
System.out.println(flatMapResult);
// [1, 2, 3, 4, 5, 6]
flatMap을 쓰면 내부의 Stream들을 하나로 합쳐 List<Integer>를 얻을 수 있다
- outerList.stream()
List<List<Integer>>에서 바깥쪽 리스트가 스트림으로 변환되어 Stream<List<Integer>>가 된다. 즉, 스트림 안에는 3개의 List<Integer> 요소가 들어 있다 - flatMap(list -> list.stream())
각 List<Integer>를 Stream<Integer>로 바꾸는 동시에, 내부 스트림의 요소를 꺼내 외부 스트림에 합친다. 결과적으로 1, 2, 3, 4, 5, 6이 하나의 Stream<Integer>로 이어진다 (map과 달리 중첩 스트림을 병함) - toList()
Stream<Integer>를 수집하여 List<Integer>로 변환한다 최종 결과는 [1, 2, 3, 4, 5, 6] 형태로 반환한다