Contents
see ListStream 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() 필수