Java
[Java] 스트림으로 데이터 수집 - 요약 연산
snail voyager
2023. 8. 5. 20:07
728x90
반응형
컬렉터
- Stream.collect 메서드의 인수로 스트림의 항목을 컬렉션으로 재구성
- 컬렉터로 스트림의 모든 항목을 하나의 결과로 합침
- Collector 인터페이스 구현은 스트림의 요소를 어떤 식으로 도출할지 지정
- collect로 결과를 수집하는 과정을 간단하면서도 유연한 방식으로 정의할 수 있음
- 스트림에 collect를 호출하면 스트림의 요소에 리듀싱 연산이 수행
- 리듀싱 연산을 이용해서 스트림의 각 요소를 방문하면서 컬렉터가 작업을 처리
- Collectors 유틸리티 클래스는 자주 사용하는 Collector 인스턴스를 생성할 수 있는 정적 팩토리 메서드 제공
Collectos에서 미리 정의된 컬렉터
- 스트림 요소를 하나의 값으로 리듀스하고 요약
- 요소 그룹화
- 요소 분할
최대값과 최소값 검색
- Collectors.maxBy, Collectors.minBy 인수로 Comparator 사용
- Optional 반환하여 값이 존재하지 않을 수 있음
Comparator<Dish> dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories);
Optional<Dish> mostCalorieDish = menu.stream().collect(maxBy(dishCaloriesComparator));
summingInt
- 스트림(Stream)의 요소들을 정수 필드를 기준으로 합산하여 총합을 구하는 Collectors 클래스의 메서드
- ToIntFunction<T> 타입을 인자로 받으며, 이는 요소들을 정수 값으로 변환하는 람다식
- summingLong, summingDouble
- averagingInt, averagingLong, averagingDouble
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));
summarizingInt
- 스트림의 요소들에 대해 최소값, 최대값, 합계, 평균 등의 요약 정보를 한 번에 얻을 수 있다
- IntSummaryStatistics 객체를 반환하며, IntSummaryStatistics 객체에는 스트림의 정수 요소들에 대한 다양한 통계 정보가 저장
- 통계 정보를 간편하게 얻을 수 있도록 도와주며, 스트림 요소들의 합계, 평균 등을 빠르게 계산
- summarizingLong, summarizingDouble, LongSummaryStatics, DoubleSummaryStatics
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
IntSummaryStatistics statistics = numbers.stream()
.collect(Collectors.summarizingInt(num -> num));
System.out.println("Count: " + statistics.getCount());
System.out.println("Sum: " + statistics.getSum());
System.out.println("Min: " + statistics.getMin());
System.out.println("Max: " + statistics.getMax());
System.out.println("Average: " + statistics.getAverage());
joining 문자열 연결
- 스트림의 각 객체에 toString 메서드를 호출해서 추출한 모든 문자열을 하나의 문자열로 연결해서 반환
- 내부적으로 StringBuilder를 이용해서 문자열을 합침
String shortMenu = menu.stream().map(Dish::getName).collect(joining());
String shortMenu = menu.stream().map(Dish::getName).collect(joining(", ")); //구분자 추가
toMap() Map 수집
- Collectors.toMap() 메서드는 스트림 요소를 Map으로 수집하는 데 사용
- 세 번째 인수인 병합 함수 사용, 키 충돌이 발생할 때 두 값을 합치는 방법
public class MergeCollisionExample {
public static void main(String[] args) {
List<Person> people = List.of(
new Person(1, "Alice"),
new Person(2, "Bob"),
new Person(3, "Charlie"),
new Person(2, "David") // 키 충돌을 일으킬 요소
);
// 스트림을 사용하여 ID를 키로, 이름을 값으로 하는 Map을 생성하며 키 충돌 발생 시 값을 병합
Map<Integer, String> idToNameMap = people.stream()
.collect(Collectors.toMap(
Person::getId,
Person::getName,
(existing, replacement) -> existing + ", " + replacement // 병합 시 새로운 값을 기존 값 뒤에 추가
));
// 결과 출력
idToNameMap.forEach((id, name) -> System.out.println("ID: " + id + ", Name: " + name));
}
}
reducing 범용 리듀싱 요약 연산
- 첫번째 인수는 초기값, 두번째 인수는 변환 함수, 세번째 인수는 BinaryOperator 결합 함수
- 한 개의 인수를 갖는 reducing 메소드는 Optional 객체를 반환
int totalCalories = menu.stream().collect(reducing(0, Dish::getCalories, (i,j)-> i+j));
Optional<Dish> mostCalorieDish = menu.stream().collect(reducing(
(d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2));
Stream 인터페이스의 collect() vs. reduce()
- collect()는 도출하려는 결과를 누적하는 컨테이너를 바꾸도록 설계된 메서드
- reduce()는 두 값을 하나로 도출하는 불변형 연산
- 가변 컨테이너 관련 작업이면서 병렬성을 확보하려면 collect()로 리듀싱 연산을 구현하는 것이 바람직
상황에 맞는 최적의 해법 선택
- 컬렉터를 이용하는 코드가 더 복잡
- 대신 재사용성과 커스터마이즈 가능성을 제공하는 높은 수준의 추상화와 일반화를 얻을 수 있음
int totalCalories = menu.stream().collect(reducing(0, Dish::getCalories, Integer::sum);
int totalCalories = menu.stream().map(Dish::getCalories).reduce(Integer::sum).get();
int totalCalories = menu.stream().mapToInt(Dish::getCalories).sum(); //간결하고 가독성이 좋음
728x90
반응형