[JAVA] Stream의 map과 flatMap 차이
Java Stream API를 사용하다 보면 자주 마주치게 되는 map()
과 flatMap()
메서드.
얼핏 비슷해 보이는 이 두 메서드의 차이점을 정확히 이해하기 위한 글입니다..
map() 메서드 이해하기
map()
은 스트림의 각 요소를 변환하여 새로운 요소로 매핑하는 중간 연산입니다. 1:1 매핑이라고 생각하면 됩니다.
기본적인 map() 사용 예제
List<String> names = Arrays.asList("john", "jane", "mike");
// 모든 이름을 대문자로 변환
List<String> upperNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upperNames); // [JOHN, JANE, MIKE]
객체 변환 예제
class User {
private String name;
private int age;
// 생성자, getter, setter 생략
}
List<User> users = Arrays.asList(
new User("john", 30),
new User("jane", 25)
);
// User 객체에서 이름만 추출
List<String> userNames = users.stream()
.map(User::getName)
.collect(Collectors.toList());
System.out.println(userNames); // [john, jane]
flatMap() 메서드 이해하기
flatMap()
은 스트림의 각 요소를 스트림으로 변환한 후, 모든 스트림을 하나의 스트림으로 평면화합니다.
1:N 매핑이라고 볼 수 있습니다.
기본적인 flatMap() 사용 예제
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d")
);
// 중첩 리스트를 단일 리스트로 평면화
List<String> flatList = nestedList.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
System.out.println(flatList); // [a, b, c, d]
실전 예제
class Order {
private List<Item> items;
// 생성자, getter, setter 생략
}
class Item {
private String name;
private double price;
// 생성자, getter, setter 생략
}
List<Order> orders = Arrays.asList(
new Order(Arrays.asList(
new Item("Apple", 1.0),
new Item("Banana", 2.0)
)),
new Order(Arrays.asList(
new Item("Orange", 3.0)
))
);
// 모든 주문에서 상품 이름만 추출
List<String> itemNames = orders.stream()
.flatMap(order -> order.getItems().stream())
.map(Item::getName)
.collect(Collectors.toList());
System.out.println(itemNames); // [Apple, Banana, Orange]
map()과 flatMap()의 주요 차이점
1. 반환 타입
map()
: 각 요소를 변환한 스트림 반환flatMap()
: 각 요소를 변환한 스트림들을 하나의 스트림으로 평면화하여 반환
2. 데이터 처리 방식
// map() 사용 - 중첩 구조 유지
List<String[]> result1 = words.stream()
.map(word -> word.split(""))
.collect(Collectors.toList());
// 결과: [[H, e, l, l, o], [W, o, r, l, d]]
// flatMap() 사용 - 평면화
List<String> result2 = words.stream()
.flatMap(word -> Arrays.stream(word.split("")))
.collect(Collectors.toList());
// 결과: [H, e, l, l, o, W, o, r, l, d]
활용 사례
1. 데이터 변환 (map 활용)
// 사용자 목록에서 이메일 도메인만 추출
List<String> domains = users.stream()
.map(User::getEmail)
.map(email -> email.split("@")[1])
.distinct()
.collect(Collectors.toList());
2. 중첩 데이터 처리 (flatMap 활용)
// 부서별 직원 목록에서 모든 직원의 이메일 추출
Map<String, List<Employee>> deptEmployees = getDepartmentEmployees();
List<String> allEmails = deptEmployees.values().stream()
.flatMap(List::stream)
.map(Employee::getEmail)
.collect(Collectors.toList());
성능상 고려사항
- 메모리 사용
flatMap()
은 중간 스트림을 생성하므로 메모리 사용량이 더 많을 수 있음- 대용량 데이터 처리 시 주의 필요
- 처리 속도
- 단순 변환은
map()
이 더 빠름 - 복잡한 중첩 구조는
flatMap()
이 효율적
- 단순 변환은
// 대용량 데이터 처리 시 병렬 스트림 활용
List<String> result = complexList.parallelStream()
.flatMap(data -> processData(data).stream())
.collect(Collectors.toList());
정리
map() 사용이 적절한 경우
- 단순 1:1 데이터 변환
- 객체의 특정 필드 추출
- 값 변환 작업
flatMap() 사용이 적절한 경우
- 중첩된 컬렉션 처리
- 복잡한 데이터 구조 평면화
- 1:N 관계의 데이터 처리
map()
과 flatMap()
은 각각의 특성과 장단점이 있다. 상황에 맞는 적절한 메서드를 선택하여 사용하면 더 효율적인 스트림 처리가 가능하다. 특히 대용량 데이터를 다룰 때는 성능적인 측면도 고려하여 선택하는 것이 중요하다.
'개발&프로그래밍' 카테고리의 다른 글
[JAVA] 직렬화(Serializable)와 역직렬화 (6) | 2024.11.03 |
---|---|
[JAVA] equals()와 hashCode() 메서드 (0) | 2024.11.02 |
[JAVA] Java 컬렉션 프레임워크 성능 비교 (ArrayList vs LinkedList vs HashSet) (1) | 2024.10.24 |
[JAVA] Java에서 NullPointerException을 방지하는 팁 (1) | 2024.10.23 |
[python] Python의 제어 흐름 : if문, for문, range, break, continue, match (3) | 2024.10.23 |
댓글