Stream은 데이터를 파이프라인처럼 연결해서 처리하는 방법
데이터 소스 → 중간 연산 → 최종 연산
(배열/리스트) (가공/필터) (결과 추출)
1. 스트림 생성
// 배열에서 생성
int[] arr = {1, 2, 3, 4, 5};
Arrays.stream(arr)
// 리스트에서 생성
List<String> list = List.of("A", "B", "C");
list.stream()
2. 중간 연산 (가공)
map - 각 요소를 변환
// int → String 변환
Arrays.stream(numbers)
.mapToObj(String::valueOf)
// [1, 2, 3] → ["1", "2", "3"]
// 각 요소에 2 곱하기
Arrays.stream(arr)
.map(x -> x * 2)
// [1, 2, 3] → [2, 4, 6]
// 문자열 대문자로 변환
list.stream()
.map(String::toUpperCase)
// ["a", "b"] → ["A", "B"]
filter - 조건에 맞는 요소만 추출
// 짝수만 필터링
Arrays.stream(arr)
.filter(x -> x % 2 == 0)
// [1, 2, 3, 4, 5] → [2, 4]
// 길이가 3 이상인 문자열만
list.stream()
.filter(s -> s.length() >= 3)
// ["AB", "ABC", "ABCD"] → ["ABC", "ABCD"]
sorted - 정렬
// 기본 오름차순
stream.sorted()
// [3, 1, 2] → [1, 2, 3]
// 내림차순
stream.sorted(Comparator.reverseOrder())
// [3, 1, 2] → [3, 2, 1]
// 커스텀 정렬 (문자열 길이 순)
stream.sorted((a, b) -> a.length() - b.length())
distinct / limit / skip
// 중복 제거
stream.distinct()
// [1, 1, 2, 2, 3] → [1, 2, 3]
// 앞에서 3개만
stream.limit(3)
// [1, 2, 3, 4, 5] → [1, 2, 3]
// 앞에서 2개 건너뛰기
stream.skip(2)
// [1, 2, 3, 4, 5] → [3, 4, 5]
3. 최종 연산 (결과 추출)
toArray - 배열로 변환
// int 배열로
int[] result = Arrays.stream(arr)
.toArray();
// String 배열로
String[] result = list.stream()
.toArray(String[]::new);
collect - 리스트/맵으로 변환
// List로 변환
List<Integer> result = Arrays.stream(arr)
.filter(x -> x > 2)
.collect(Collectors.toList());
// [1, 2, 3, 4, 5] → [3, 4, 5]
// Map으로 변환
Map<String, Integer> result = list.stream()
.collect(Collectors.toMap(
s -> s, // key
s -> s.length() // value
));
// ["AB", "ABC"] → {"AB":2, "ABC":3}
reduce - 요소를 하나로 합치기
// 전체 합계
int sum = Arrays.stream(arr)
.reduce(0, (a, b) -> a + b);
// [1, 2, 3, 4, 5] → 15
// 전체 곱
int product = Arrays.stream(arr)
.reduce(1, (a, b) -> a * b);
// [1, 2, 3, 4, 5] → 120
첫 번째 인자는 초기값(identity) ➡︎ 연산 결과에 영향을 주지 않는 값
- 덧셈 → 초기값 0 (0 + x = x)
- 곱셈 → 초기값 1 (1 * x = x)
- 최댓값 → 초기값 Integer.MIN_VALUE
- 최솟값 → 초기값 Integer.MAX_VALUE
숫자 관련 최종 연산
// 합계
Arrays.stream(arr).sum() // 15
// 평균
Arrays.stream(arr).average() // 3.0
// 최댓값
Arrays.stream(arr).max() // 5
// 최솟값
Arrays.stream(arr).min() // 1
// 개수
Arrays.stream(arr).count() // 5
forEach / anyMatch / allMatch
// 각 요소 출력
stream.forEach(System.out::println);
// 하나라도 조건 만족?
boolean result = stream.anyMatch(x -> x > 3); // true
// 전부 조건 만족?
boolean result = stream.allMatch(x -> x > 0); // true
4. 코테에서 자주 쓰이는 패턴
// int[] → List<Integer>
List<Integer> list = Arrays.stream(arr)
.boxed()
.collect(Collectors.toList());
// List<Integer> → int[]
int[] arr = list.stream()
.mapToInt(Integer::intValue)
.toArray();
// 2차원 배열 평탄화
int[] flat = Arrays.stream(matrix)
.flatMapToInt(Arrays::stream)
.toArray();
// [[1,2],[3,4]] → [1,2,3,4]
// 문자열 → 각 문자 배열
"hello".chars()
.mapToObj(c -> String.valueOf((char) c))
.toArray(String[]::new);
// ["h","e","l","l","o"]
한눈에 보는 흐름
[1, 2, 3, 4, 5]
↓ .filter(x -> x % 2 == 0)
[2, 4]
↓ .map(x -> x * 10)
[20, 40]
↓ .collect(Collectors.toList())
List<Integer> [20, 40]
Stream은 한 번 쓰고 버리는 일회용이라서, 같은 스트림을 두 번 사용하면 에러(필요하면 새로 생성)
'Spring' 카테고리의 다른 글
| 모여모여 트러블 슈팅 모음 (0) | 2026.02.11 |
|---|---|
| JPA N+1 문제 (0) | 2025.10.23 |
| EntityManager (엔티티 매니저) (0) | 2025.10.22 |
| JPA ddl-auto 옵션 (0) | 2025.10.21 |
| Spring Data JPA에서 새로운 Entity인지 판단하는 방법 (0) | 2025.10.20 |