Stream API - map과 flatMap

urur-27·2025년 3월 3일
0

StreamAPI

목록 보기
6/6

mapflatmap은 스트림의 요소를 변환하거나 평탄화하는 데 사용되는 중간 연산.
map요소를 변환하는 데 사용되며, flatMap리스트, 배열, Optional과 같은 중첩된 구조를 단순화할 때 주로 사용된다.

  • 차이점

    map()
    -> 변환 후에도 스트림의 구조를 유지 (즉, Stream<T>Stream<U>로 변환)
    -> 한 개의 입력 값 → 한 개의 출력 값 (1:1 매핑)
    map() 사용처:
    -> 단순 변환 (e.g. StringInteger)
    -> 리스트에서 특정 필드 추출 (List<User>List<String>)

    flatMap()
    -> 변환 후 스트림을 평탄화 (즉, Stream<Stream<U>>Stream<U>로 변환)
    -> 한 개의 입력 값 → 여러 개의 출력 값 (1:N 매핑, 중첩 제거)
    -> 스트림 내부의 스트림을 하나의 스트림으로 만들어야 할 때 사용
    flatMap() 사용처:
    -> 리스트의 리스트 평탄화 (List<List<T>>List<T>)
    -> 문자열을 단어로 변환 (StringList<String>)
    -> Optional<Optional<T>> 평탄화


1. map 메서드

map은 스트림의 각 요소에 함수를 적용하여 새로운 요소로 변환시킴. 각 입력 요소에 대해 하나의 출력 요소를 생성하므로, 스트림의 길이는 변하지 않음.

일반적으로 객체의 특정 필드 추출, 데이터 변환, 데이터 가공 등의 목적으로 사용된다.

map() 예제:

List<String> words = Arrays.asList("apple", "banana", "cherry");

List<Integer> wordLengths = words.stream()
    .map(String::length)  // 각 단어의 길이로 변환
    .collect(Collectors.toList());

System.out.println(wordLengths); // 출력: [5, 6, 6]
  • 실행 과정
    1) "apple"5
    2) "banana"6
    3) "cherry"6
    4) 결과: [5, 6, 6]

  • map()의 특징
    변환된 요소는 하나씩 그대로 유지됨.
    Stream<T>Stream<U> 변환.


2. map 메서드 활용 예시

1) 객체의 특정 필드 추출:

class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
}

List<User> users = Arrays.asList(new User("Alice", 25), new User("Bob", 30));
List<String> names = users.stream()
    .map(User::getName)
    .collect(Collectors.toList());

System.out.println(names); // 출력: [Alice, Bob]
  • map(User::getName)을 통하여 User 객체에서 name 값만 추출

2) 데이터 변환:

List<String> numbers = Arrays.asList("1", "2", "3");
List<Integer> intNumbers = numbers.stream()
    .map(Integer::parseInt)
    .collect(Collectors.toList());

System.out.println(intNumbers); // 출력: [1, 2, 3]
  • map(Integer::parseInt) 메서드를 이용하여 String 값을 Integer로 변경

3) 데이터 가공:

List<String> words = Arrays.asList("hello", "world");
List<String> upperWords = words.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());

System.out.println(upperWords); // 출력: [HELLO, WORLD]
  • map(String::toUpperCase)을 통하여 문자열을 대문자로 변환시킴

3. flatMap 메서드

flatMap 메서드도 map 메서드처럼 각 요소에 함수를 적용하여 새로운 요소로 변환시킨다. 다만, 다른 점은 생성된 여러 스트림을 하나의 스트림으로 평탄화(flatten)한다. 즉, 중첩된 스트림 구조를 단일 스트림으로 변환한다.

flatMap() 예제:

List<List<String>> nestedList = Arrays.asList(
    Arrays.asList("Alice", "Bob"),
    Arrays.asList("Charlie", "David"),
    Arrays.asList("Eve", "Frank")
);

List<String> flatList = nestedList.stream()
    .flatMap(List::stream) // 리스트 내부의 리스트를 풀어서 하나의 스트림으로 만듦
    .collect(Collectors.toList());

System.out.println(flatList); // 출력: [Alice, Bob, Charlie, David, Eve, Frank]
  • 실행 과정
    1) "Alice", "Bob"Stream.of("Alice", "Bob")
    2) "Charlie", "David"Stream.of("Charlie", "David")
    3) "Eve", "Frank"Stream.of("Eve", "Frank")
    4) 모든 Stream<String>을 합쳐서 하나의 Stream<String>으로 만듦.
    5) 결과: ["Alice", "Bob", "Charlie", "David", "Eve", "Frank"]
  • flatMap()의 특징
    Stream<List<T>>Stream<T> 변환 (즉, 중첩된 구조를 평탄화).

4. flatMap 메서드 활용 예시

1) 리스트 속 여러 리스트들을 하나의 리스트로 평탄화

List<List<String>> nestedList = Arrays.asList(
    Arrays.asList("Alice", "Bob"),
    Arrays.asList("Charlie", "David"),
    Arrays.asList("Eve", "Frank")
);

List<String> flatList = nestedList.stream()
    .flatMap(List::stream) // 리스트 내부의 리스트를 풀어서 하나의 스트림으로 만듦
    .collect(Collectors.toList());

System.out.println(flatList); // 출력: [Alice, Bob, Charlie, David, Eve, Frank]
  • flatMap(List::stream) 메서드를 이용하여 단일 리스트로 평탄화 시킨 예시

2) 문장을 단어 단위로 나누어 리스트로 평탄화:

List<String> sentences = Arrays.asList("Hello World", "Java Stream API");

List<String> words = sentences.stream()
    .flatMap(sentence -> Arrays.stream(sentence.split(" ")))
    .collect(Collectors.toList());

System.out.println(words); // 출력: [Hello, World, Java, Stream, API]
  • split(" ")을 이용해 문장을 단어 단위로 분할한 후, flatMap으로 평탄화

3) Optional과 함께 사용 (중첩된 Optional 제거)

Optional<Optional<String>> optional = Optional.of(Optional.of("Hello"));

Optional<String> flatOptional = optional.flatMap(o -> o);

System.out.println(flatOptional.get()); // 출력: Hello
  • Optional<Optional<T>>flatMap을 사용하여 Optional<T>로 변환. 내부를 펼쳐서(flat) 반환하기 때문에 이렇게 사용 가능
  • 여기서 map을 사용했다면 .get()을 두 번 호출해야 "Hello"에 접근 가능

4) 데이터베이스 조회 결과 평탄화

class Order {
    private String productName;

    public Order(String productName) {
        this.productName = productName;
    }

    public String getProductName() {
        return productName;
    }
}

class User {
    private String name;
    private List<Order> orders;

    public User(String name, List<Order> orders) {
        this.name = name;
        this.orders = orders;
    }

    public List<Order> getOrders() {
        return orders;
    }
}

List<User> users = Arrays.asList(
    new User("Alice", Arrays.asList(new Order("Laptop"), new Order("Mouse"))),
    new User("Bob", Arrays.asList(new Order("Keyboard"), new Order("Monitor")))
);

List<String> allOrders = users.stream()
    .flatMap(user -> user.getOrders().stream())
    .map(Order::getProductName)
    .collect(Collectors.toList());

System.out.println(allOrders); // 출력: [Laptop, Mouse, Keyboard, Monitor]
  • 여러 개의 주문을 가지고 있을 때, 전체 주문 리스트를 하나의 리스트로 만드는 예시
  • 여러 유저의 주문 목록을 단일 리스트로 변환하는 데 flatMap 사용
profile
끄아악

0개의 댓글