728x90
요약:map은 스트림의 각 요소를 다른 값으로 1:1 변환하는 연산입니다. 이름을 대문자로, 객체를 특정 필드로, 문자열을 길이(숫자)로 — "입력 하나당 출력 하나"가 핵심입니다. 이 글에서map의 기본 사용법,for루프 대비 장점, 그리고 메서드 참조(String::toUpperCase) 와 "개수는 그대로 유지된다"는 특성까지 정리합니다.
스트림을 만들었으면 다음은 가공입니다. 그중 가장 많이 쓰는 연산이 바로 map이죠. "상품 리스트에서 이름만 뽑아라", "가격에 부가세를 붙여라", "DTO를 엔티티로 바꿔라" 같은 요구가 전부 map 한 줄로 끝납니다.
면접에서도 "map과 filter의 차이를 설명하라" 는 질문이 단골입니다. 한마디로 filter는 요소를 거르고(개수가 줄 수 있음), map은 요소를 바꿉니다(개수는 그대로). 이 차이를 코드로 확실히 잡아 보겠습니다.

문제 정의
문제. 소문자 이름 리스트가 주어진다. ① 모두 대문자로 바꾼 리스트와 ② 각 이름의 글자 수 리스트를 각각 만들어라.
- 입력:
List<String> names = ["kim", "lee", "park"] - 기대 출력:
- 대문자:
[KIM, LEE, PARK] - 길이:
[3, 3, 4]
전통 방식(for-loop) — 그리고 스트림이 나은 이유
스트림이 없다면 결과를 담을 빈 리스트를 미리 만들고, 루프를 돌며 하나씩 변환해 add 해야 합니다.
import java.util.ArrayList;
import java.util.List;
public class Traditional {
public static void main(String[] args) {
List<String> names = List.of("kim", "lee", "park");
List<String> upper = new ArrayList<>();
for (String name : names) {
upper.add(name.toUpperCase()); // 변환해서 새 리스트에 담기
}
System.out.println(upper);
}
}
동작은 하지만 "빈 리스트 생성 → 루프 → add" 라는 상투적인 뼈대가 매번 반복됩니다. 정작 중요한 로직(toUpperCase)은 이 뼈대 속에 묻히죠. map은 이 과정을 "각 요소를 이렇게 바꿔라" 한 줄로 압축합니다.
스트림 해법 — map

import java.util.List;
import java.util.stream.Collectors;
public class MapExample {
public static void main(String[] args) {
List<String> names = List.of("kim", "lee", "park");
// ① 각 이름을 대문자로 (String -> String)
List<String> upper = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upper);
// ② 각 이름을 길이로 (String -> Integer)
List<Integer> lengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println(lengths);
}
}
한 줄씩 설명
names.stream()— 리스트에서 스트림을 엽니다(1편 참고)..map(String::toUpperCase)— 각 요소에toUpperCase()를 적용해 새 스트림으로 흘려보냅니다.String::toUpperCase는 람다s -> s.toUpperCase()의 메서드 참조 축약형입니다..map(String::length)— 이번엔String을 받아Integer를 반환합니다. 이렇게map은 입력 타입과 출력 타입이 달라도 됩니다(String→Integer)..collect(Collectors.toList())— 변환된 요소들을 다시 리스트로 모읍니다(5편에서 자세히 다룹니다).
실행 결과
[KIM, LEE, PARK]
[3, 3, 4]
변형·함정·주의
map은 개수를 바꾸지 않는다. 입력 3개면 출력도 정확히 3개입니다. "거르기"가 필요하면map이 아니라filter(3편)입니다.- 숫자 변환엔
mapToInt가 유리.map(String::length)는Stream<Integer>를 만들지만, 바로 합계를 낼 거라면mapToInt(String::length)로IntStream을 만들어.sum()을 바로 쓰는 편이 박싱 오버헤드가 없습니다(6편 참고). - 중첩 구조엔
map이 아니라flatMap. 한 요소가 여러 값으로 펼쳐져야 한다면(예: 문장 → 단어들)map이 아니라flatMap을 씁니다(11편). - 메서드 참조 4종.
String::toUpperCase(인스턴스 메서드),Integer::parseInt(정적 메서드),User::new(생성자),obj::method(특정 객체 메서드) — 람다를 더 짧게 쓰는 문법으로 익혀 두면 좋습니다.
관련 면접 질문
- Q1.
map과filter의 차이는?
→ map은 각 요소를 다른 값으로 변환(개수 유지), filter는 조건에 맞는 요소만 선택(개수 감소 가능). map은 Function, filter는 Predicate를 인자로 받습니다.
- Q2.
map과flatMap의 차이는?
→ map은 요소 하나를 값 하나로(1:1), flatMap은 요소 하나를 스트림(0..N개) 으로 펼친 뒤 평탄화합니다(1:N). 리스트의 리스트를 한 겹으로 펼 때 flatMap을 씁니다.
시리즈 다시보기
- ⬆️ 📎 목차 허브 — 자바 스트림 코딩 문제 41선 총정리 (0편으로 돌아가기)
- ⬅️ 📎 이전 편 — [자바 스트림 1] 스트림 생성 5가지 방법
- ➡️ 📎 다음 편 — [자바 스트림 3] filter로 조건 걸러내기 (발행 시 연결)
- 📎 함께 읽기 — Java 25 LTS 새 기능 총정리 (최신 자바 문법 레퍼런스)
요소를 "바꾸는"map을 익혔으니, 다음 편에서는 요소를 "고르는"filter로 넘어갑니다. 이 둘을 엮으면 스트림의 절반은 끝난 셈입니다.
'쿤즈 Dev > Java' 카테고리의 다른 글
| [자바 스트림 4] forEach로 순회하기 — 스트림의 종착역과 함정 (0) | 2026.10.20 |
|---|---|
| [자바 스트림 3] filter로 조건 걸러내기 — 원하는 것만 쏙 뽑기 (0) | 2026.10.19 |
| 자바 스트림 코딩 문제 41선 총정리 — 면접·코테 완전정복 [목차] (0) | 2026.10.17 |
| Java 25 LTS 새 기능 총정리 — 21에서 25로, 뭐가 달라졌나 (마이그레이션 체크리스트) (0) | 2026.10.15 |
| [Java] Collection API 데이터를 다루는 인터페이스와 클래스의 집합 (0) | 2023.10.08 |
댓글