Contents
see List작성일 2025. 11. 30.
Stream API 고급 활용법
Java Stream API의 고급 기능들을 활용하여 복잡한 데이터 처리를 선언적이고 효율적으로 수행하는 방법입니다.
언제 사용하나요?
- 컬렉션 데이터 필터링, 변환, 집계
- 복잡한 데이터 그룹핑
- 병렬 처리로 성능 향상
- 함수형 스타일 데이터 파이프라인
Collectors 고급 활용
List<User> users = getUsers();
// 그룹핑 + 카운팅
Map<String, Long> countByDept = users.stream()
.collect(Collectors.groupingBy(
User::getDepartment,
Collectors.counting()
));
// 그룹핑 + 합계
Map<String, Integer> salaryByDept = users.stream()
.collect(Collectors.groupingBy(
User::getDepartment,
Collectors.summingInt(User::getSalary)
));
// 다중 그룹핑
Map<String, Map<String, List<User>>> byDeptAndRole = users.stream()
.collect(Collectors.groupingBy(
User::getDepartment,
Collectors.groupingBy(User::getRole)
));
// 파티셔닝 (true/false 분류)
Map<Boolean, List<User>> partitioned = users.stream()
.collect(Collectors.partitioningBy(u -> u.getSalary() > 50000));flatMap으로 중첩 컬렉션 처리
List<Order> orders = getOrders();
// 모든 주문의 상품 목록 추출
List<Product> allProducts = orders.stream()
.flatMap(order -> order.getProducts().stream())
.distinct()
.collect(Collectors.toList());
// 문자열 분리 후 단어 추출
List<String> sentences = Arrays.asList("Hello World", "Stream API");
List<String> words = sentences.stream()
.flatMap(s -> Arrays.stream(s.split(" ")))
.collect(Collectors.toList());reduce로 커스텀 집계
// 최댓값 찾기
Optional<User> highestPaid = users.stream()
.reduce((u1, u2) -> u1.getSalary() > u2.getSalary() ? u1 : u2);
// 문자열 연결
String names = users.stream()
.map(User::getName)
.reduce("", (a, b) -> a.isEmpty() ? b : a + ", " + b);
// 병렬 reduce (combiner 필수)
int total = users.parallelStream()
.map(User::getSalary)
.reduce(0, Integer::sum, Integer::sum);Optional과 함께 사용
// findFirst + orElse
User firstAdmin = users.stream()
.filter(u -> "ADMIN".equals(u.getRole()))
.findFirst()
.orElse(defaultUser);
// findFirst + orElseThrow
User admin = users.stream()
.filter(u -> "ADMIN".equals(u.getRole()))
.findFirst()
.orElseThrow(() -> new NotFoundException("Admin not found"));
// Optional 체이닝
String city = users.stream()
.filter(u -> u.getId() == 1)
.findFirst()
.map(User::getAddress)
.map(Address::getCity)
.orElse("Unknown");정렬 (다중 조건)
// 여러 조건으로 정렬
List<User> sorted = users.stream()
.sorted(Comparator
.comparing(User::getDepartment)
.thenComparing(User::getSalary, Comparator.reverseOrder())
.thenComparing(User::getName))
.collect(Collectors.toList());
// null 처리
users.stream()
.sorted(Comparator.comparing(
User::getNickname,
Comparator.nullsLast(Comparator.naturalOrder())
));병렬 스트림
// 대용량 데이터 병렬 처리
long count = largeList.parallelStream()
.filter(item -> item.getValue() > threshold)
.count();
// 주의: 순서 보장 필요시
List<String> ordered = list.parallelStream()
.map(String::toUpperCase)
.collect(Collectors.toList()); // 순서 유지됨
// 성능 측정
long start = System.currentTimeMillis();
list.parallelStream().forEach(this::process);
System.out.println("Time: " + (System.currentTimeMillis() - start));커스텀 Collector
// 커스텀 Collector 생성
Collector<User, ?, Statistics> statsCollector = Collector.of(
Statistics::new, // supplier
Statistics::accept, // accumulator
Statistics::combine, // combiner
Collector.Characteristics.UNORDERED
);
Statistics stats = users.stream().collect(statsCollector);peek으로 디버깅
List<String> result = names.stream()
.peek(n -> System.out.println("Original: " + n))
.map(String::toUpperCase)
.peek(n -> System.out.println("Uppercase: " + n))
.filter(n -> n.length() > 3)
.peek(n -> System.out.println("Filtered: " + n))
.collect(Collectors.toList());주의사항
- Stream은 한 번만 사용 가능 (재사용 X)
- 병렬 스트림은 작은 데이터에 오히려 느림
- 상태를 가진 람다 사용 시 주의
- 무한 스트림은 limit() 필수
java
| No | 작성일 | Title |
|---|---|---|
| 3356 | 2026. 08. 14. | Java record와 sealed interface로 안전한 API 응답 모델 만들기 |
| 3324 | 2026. 08. 05. | Java 가상 스레드 실전 적용 가이드: I/O 중심 API의 동시성 구조 바꾸기 |
| 3292 | 2026. 07. 28. | Java 메모리 사용량이 계속 늘어날 때: Heap Dump와 JFR로 누수 원인 찾기 |
| 3261 | 2026. 07. 20. | Java record로 API DTO를 단순하게 만드는 방법: 검증·변환·직렬화 실전 적용 |
| 3235 | 2026. 07. 12. | Java API 동시 호출 폭주 대응: Thread Pool, timeout, 장애 격리로 안정성 올리기 |
| 3178 | 2026. 07. 03. | Java 메모리 누수 진단 가이드: jcmd와 JFR로 원인 좁히기 |
| 3130 | 2026. 06. 25. | Java 가상 스레드 적용 전 점검: 블로킹 I/O와 동시성 제한 운영 가이드 |
| 3074 | 2026. 06. 17. | Java 스레드 작업 취소 설계: interrupt, Future, ExecutorService 종료를 제대로 연결하기 |
| 2985 | 2026. 06. 09. | Java 컨테이너 메모리 운영 가이드: 힙, 직접 메모리, GC 로그를 함께 점검하기 |
| 2928 | 2026. 06. 01. | Java Virtual Threads 운영 전환 가이드: 블로킹 API, 커넥션 풀, 관측 지표 점검법 |