
SOFTMOA TECHNOLOGY
Stream API 고급 활용법
소프트모아가 정리한 기술 기록입니다.
Contents
작성일 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() 필수
Share this
Java
Showing 1 to 10 of 68 entries
