Java

[Java] Stream - map과 flatMap

Castle Bird 2025. 11. 26. 11:55

1. Map 이란?

  • map은 자바의 stream객체에서만 사용할 수 있는 중간 연산 메서드이다.
  • stream은 각 요소를 순차적으로 접근하며, map은 각 요소마다 1:1로 함수형 인터페이스 Function<T, R>에 정의한 함수를 적용하여 return한다.
    • Function<T, R>란: 매개변수 1개(T 타입), 리턴 1개(R 타입)의 함수형 인터페이스이다.
  • map이 반환하는 것은 새로운 Stream<R>이 되며, 다음 연산으로 사용된다.

아래는 예시 코드이다.

List<String> list = List.of("apple", "banana", "cat");

// 람다식
List<Integer> intList1 = list.stream()
        .map(str -> str.length())
        .toList();

// 메서드 참조형식
List<Integer> intList2 = list.stream()
        .map(String::length)
        .toList();

System.out.println(intList1); // [5, 6, 3]
System.out.println(intList2); // [5, 6, 3]

 

코드를 하나씩 살펴보자

리스트 준비

List<String> list = List.of("apple", "banana", "cat");
  • 기본 List이다.

람다식

List<Integer> intList1 = list.stream().map(str -> str.length()) .toList();
  • list.stream()Stream<String> 객체를 생성하며, 내부적으로 요소를 순차적으로 처리한다.
  • map(str -> str.length())는 스트림의 각 요소(String) 를 1:1로 받아 Integer로 변환한다.
  • 변환된 값들은 .toList()에 의해 새로운 List<Integer>수집되며, 최종 결과가 intList1이 된다.

메서드 참조식

List<Integer> intList2 = list.stream().map(String::length).toList();
  • 람다식과 100% 같은 기능을 한다.
  • str -> str.length()의 람다식은 입력 파라미터 한 개(T), 반환값 한 개(R) 를 가지며 String::length 메서드의 시그니처와 정확히 일치한다.
  • 따라서 람다식을 메서드 참조형으로 축약이 가능하다.

2. FlatMap 이란?

  • 각 요소를 Stream으로 변환하고, 여러 Stream하나의 Stream으로 합침
  • 즉, 중첩 구조(이중배열 등)를 제거하고 단일 스트림으로 만들어 줌

아래는 예시 코드이다.

List<String> list1 = List.of("apple1", "banana1", "cat1");
List<String> list2 = List.of("apple2", "banana2", "cat2");

// 현재 이중 리스트
List<List<String>> addList = List.of(list1, list2); // [["apple1", "banana1", "cat1"], ["apple2", "banana2", "cat2"]]


// 람다식
List<String> newList1 = addList.stream()
        .flatMap(strList -> strList.stream())
        .toList();

// 메서드 참조식
List<String> newList2 = addList.stream()
        .flatMap(List::stream)
        .toList();

System.out.println(newList1); // [apple1, banana1, cat1, apple2, banana2, cat2]
System.out.println(newList2); // [apple1, banana1, cat1, apple2, banana2, cat2]

 

코드를 하나씩 살펴보자

리스트 준비

List<String> list1 = List.of("apple1", "banana1", "cat1");
List<String> list2 = List.of("apple2", "banana2", "cat2");

// 현재 이중 리스트
List<List<String>> addList = List.of(list1, list2); 
// [["apple1", "banana1", "cat1"], ["apple2", "banana2", "cat2"]]
  • 중첩된 2차원 리스트로, 리스트 안에 또 다른 리스트들이 담겨 있음.

람다식

// 람다식
List<String> newList1 = addList.stream()
        .flatMap(strList -> strList.stream())
        .toList();
  • addList.stream()Stream<List<String>>을 생성한다.
  • flatMap(strList -> strList.stream())는 각 내부 리스트(List<String>)를 다시 스트림으로 바꾼다.
  • 이과정으로 모든 요소들이 단 하나의 Stream으로 평탄화(flatten)된다. (현재 이중배열 기준)
  • toList()를 통해 모든 Stream을 하나의 List로 변환

메서드 참조식

// 메서드 참조식
List<String> newList2 = addList.stream()
        .flatMap(List::stream)
        .toList();
  • 람다식과 100% 같은 기능을 한다.
  • strList -> strList.stream()의 람다식은 입력 파라미터 한 개(T), 반환값 한 개(R) 를 가지며 List::stream 메서드의 시그니처와 정확히 일치한다.
  • 따라서 람다식을 메서드 참조형으로 축약이 가능하다.

3. Map, FlatMap 차이점 정리

  • map()은 스트림의 각 요소를 1:1로 변환하며, 입력 구조를 그대로 유지합니다.
  • 반면 flatMap()은 각 요소를 스트림으로 변환한 뒤, 여러 스트림을 하나로 합쳐(flatten) 단일 스트림을 만듭니다.
  • 즉, map()결과가 입력 요소 수와 동일하지만, flatMap()은 하나의 요소가 여러 결과로 매핑될 수 있어 요소 수가 달라질 수 있습니다.
  • map()은 단순 값 변환이나 필드 추출에 적합하고, flatMap()은 리스트 안의 리스트처럼 중첩 구조를 평탄화할 때 유용합니다.
  • 또한, flatMap()을 사용할 때는 매핑 함수가 반드시 스트림을 반환해야 하며, 그렇지 않으면 원하는 평탄화 결과를 얻기 어렵습니다.

Javascript/React.js를 해보았다면 람다식은 익숙할 것 같다.
※ 필자는 람다식까지만 작성하고 인텔리제이가 줄여주면 그대로 사용한다. 😂

'Java' 카테고리의 다른 글

[Java] Spring의 탄생 배경  (0) 2025.12.11
[Java] HashSet  (0) 2025.12.07
[Java] 단일 책임 원칙(SRP)과 개방-폐쇄 원칙(OCP)  (0) 2025.11.25
[Java] 싱글톤 패턴  (0) 2025.11.18
[Java/Set] HashSet, LinkedHashSet, TreeSet  (0) 2025.10.21