**Stream map()**은 Java 8에서 컬렉션 요소를 변환하는 핵심 메서드로, 함수형 프로그래밍 스타일의 데이터 처리를 제공합니다.

기본 map() 사용

요소 변환

// 문자열 대문자 변환
List<String> names = Arrays.asList("john", "jane", "bob");
List<String> upperNames = names.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());
// 결과: ["JOHN", "JANE", "BOB"]
 
// 문자열 길이 추출
List<Integer> lengths = names.stream()
    .map(String::length)
    .collect(Collectors.toList());
// 결과: [4, 4, 3]
 
// 숫자 변환
List<String> numbers = Arrays.asList("1", "2", "3");
List<Integer> ints = numbers.stream()
    .map(Integer::parseInt)
    .collect(Collectors.toList());

객체 속성 추출

class User {
    String name;
    int age;
    // getters...
}
 
List<User> users = getUsers();
List<String> names = users.stream()
    .map(User::getName)
    .collect(Collectors.toList());

flatMap() 사용

리스트 평탄화

// 중첩 리스트를 단일 리스트로 변환
List<List<Integer>> nested = Arrays.asList(
    Arrays.asList(1, 2),
    Arrays.asList(3, 4)
);
 
List<Integer> flattened = nested.stream()
    .flatMap(List::stream)
    .collect(Collectors.toList());
// 결과: [1, 2, 3, 4]

문자열 분리

// 문장을 단어 단위로 분리
List<String> sentences = Arrays.asList("Hello world", "Java streams");
 
List<String> words = sentences.stream()
    .flatMap(sentence -> Arrays.stream(sentence.split(" ")))
    .collect(Collectors.toList());
// 결과: ["Hello", "world", "Java", "streams"]

map() vs flatMap()

차이점

// map(): 1:1 변환
Stream<X> → map(function) → Stream<Y>
 
// flatMap(): 1:N 변환 + 평탄화
Stream<X> → flatMap(function) → Stream<Y>
 
// 예시
List<String> words = Arrays.asList("hello", "world");
 
// map: Stream<String[]> 반환
words.stream().map(w -> w.split(""));  // [['h','e','l','l','o'], ['w','o','r','l','d']]
 
// flatMap: Stream<String> 반환 (평탄화됨)
words.stream().flatMap(w -> Arrays.stream(w.split("")));  // 'h','e','l','l','o','w','o','r','l','d'

실전 패턴

데이터 변환

// DTO 변환
List<UserDTO> dtos = users.stream()
    .map(user -> new UserDTO(user.getId(), user.getName()))
    .collect(Collectors.toList());
 
// 맵 변환
Map<String, Integer> nameToAge = users.stream()
    .collect(Collectors.toMap(User::getName, User::getAge));

체이닝과 필터링

// 변환 후 필터링
List<String> result = text.stream()
    .map(String::toUpperCase)         // 대문자 변환
    .filter(s -> s.length() > 5)     // 길이 5 초과 필터링
    .collect(Collectors.toList());
 
// 중복 제거
List<String> unique = users.stream()
    .map(User::getDepartment)
    .distinct()
    .collect(Collectors.toList());

성능 고려사항

프리미티브 스트림

// boxing 오버헤드 방지
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
 
int sum = numbers.stream()
    .mapToInt(Integer::intValue)  // IntStream으로 변환
    .sum();

지연 연산

// 스트림은 지연 연산
List<String> result = bigList.stream()
    .map(String::toUpperCase)      // 아직 실행되지 않음
    .filter(s -> s.startsWith("A"))  // 아직 실행되지 않음
    .limit(10)                       // 첫 10개만 처리
    .collect(Collectors.toList());   // 여기서 실행됨

주의사항

일반적인 실수

// ❌ 소스 수정 시도
list.stream().forEach(s -> list.remove(s));  // ConcurrentModificationException
 
// ✅ 새 컬렉션 생성
List<String> result = list.stream()
    .filter(s -> !s.equals("target"))
    .collect(Collectors.toList());
 
// ❌ 사이드 이펙트
List<String> result = new ArrayList<>();
list.stream().map(s -> { result.add(s); return s; })  // 피해야 할 패턴
 
// ✅ 순수 함수 사용
List<String> result = list.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());

관련 페이지