Java8 In Action 을 읽고 정리한 내용이다. Collectors 클래스로 컬렉션을 만들고 사용하는 방법에 대해서 설명한다.
리듀싱과 요약
요약 연산
summingXXX
Collectors
클래스는 Collectors.summingInt
라는 특별한 요약 메서드를 제공한다. 각 리스트에 선택된 값들을 합한 값으로 리턴한다.
1 | @Getter @Setter@ToString |
결과 화면
1 | 4300 |
Collectors.summingLong
과Collectors.summingDouble
도Collectors.summingInt
와 같은 방식으로 동작한다.
averagingXXX
평균값 계산 등의 연산도 요약 기능으로 제공된다.
1 | public class Reducing { |
결과 화면
1 | 477.77777777777777 |
averagingInt
,averagingLong
,averagingDouble
등으로 다양한 형식으로 이루어진 숫자 집합의 평균을 계산할 수있다.
문자열 연결
컬렉터에 joining
팩토리 메서드를 이용하면 스트림의 각 객체에 toString
메서드를 호출해서 추출한 모든 문자열을 하나의 문자열로 연결해서 반환한다. joining
메서드는 내부적으로 Stringbuilder
를 이용해서 문자열을 하나로 만든다.
1 | public class Reducing { |
결과 화면
1 | pork, beef, chicken, french fries, rice, season fruit, pizza, prawns, salmon |
범용 리듀싱 요약 연산
범용 Collectors.reducing
으로 지금까지 살펴본 모든 컬렉터를 reducing
팩토리 메서드로도 정의 할 수 있다.
1 | public class Reducing { |
결과 화면
1 | 4300 |
reducing
은 세 개의 인수를 받는다.
- 첫 번째 인수는 리듀싱 연산의 시작값이거나 스트림에 인수가 없을 때는 반환값이다(숫자 합계에서는 인수가 없을 때 반환값으로 0이 적합하다).
- 두 번째 인수는 데이터 값을 정수로 변환할 떄 사용한 변환 함수다.
- 세 번째 인수는 같은 종류의 두 항목을 하나의 값으로 더하는
BinaryOperator
다. 위에서는 두 개의int
가 사용 되었다.
한 개의 인수를 가진 reducing
가장 높은 값들을 찾을수 있다.
1 | public class Reducing { |
결과 화면
1 | Optional[Dish(name=pork, vegetarian=false, calories=800, type=MEAT)] |
그룹화
데이터 집합을 하나 이상의 특성으로 분류해서 그룹화하는 연산도 데이터베이스에서 많이 수행되는 작업이다. 자바8의 함수형을 이용하면 가독성 있는 한 줄의 코드로 그룹화를 구현할 수 있다.
다수준 그룹화
두 인수를 받는 팩토리 메서드 Collectors.groupingBy
를 이용해서 항목을 다수준으로 그룹화 할 수 있다.
1 | public class Grouping { |
결과 화면
1 | {MEAT={DIET=[Dish(name=chicken, vegetarian=false, calories=400, type=MEAT)], FAT=[Dish(name=pork, vegetarian=false, calories=800, type=MEAT)], NORMAL=[Dish(name=beef, vegetarian=false, calories=700, type=MEAT)]} |
Collectors 클래스의 정적 팩토리 메서드
팩토리 메서드 | 반환형식 | 사용 예제 | 활용 예 |
---|---|---|---|
toList | List |
스트림의 모든 항목을 리스트로 수집 | List |
toSet | Set |
스트림의 모든 항목을 중복이 없는 집합으로 수집 | Set |
toCollection | Collection |
스트림의 모든 항목을 공급자가 제공하는 컬렉션으로 수집 | Collection |
counting | Long | 스트림의 항목 수 계산 | long howManyDishes = menuStream.collect(counting()); |
summingInt | Integer | 스트림의 항목에서 정수 프로퍼티값을 더함 | int totalCalories = menuStream.collect(summingInt(Dish::getCalories)); |
averagingInt | Double | 스트림 항목의 정수 프로퍼티의 평균값 계산 | double avgCalories = menuStream.collect(averagingInt(Dish::getCalories)); |
summarizing | IntSummaryStatistics | 스트림 내의 항목의 최댓값, 최솟값, 합계, 평균 등의 정수 정보 통계를 수집 | IntSummaryStatistics menuStatistics = menuStream.collect(summarizingInt(Dish::getCalories)); |
joining | String | 스트림의 각 항목에 toString 메서드를 호출한 결과 문자열을 연결. | String shortMenu = menuStream.map(Dish::getName).collect(joining(“, “)); |
maxBy | Optional |
주어진 비교자를 이용해서 스트림의 최댓값 요소를 Optional로 감싼 값을 반환. 스트림에 요소가 없을 때는 Optional.empty()를 반환 | Optional |
minBy | Optional |
주어진 비교자를 이용해서 스트림의 최솟값 요소를 Optional로 감싼 값을 반환. 스트림에 요소가 없을 때는 Optional.empty()를 반환 | Optional |
reducing | 리듀싱 연산에서 형식을 결정 | 누적자를 초깃값으로 설정한 다음에 BinaryOperator로 스트림의 각 요소를 반복적으로 누적자와 합쳐 스트림을 하나의 값으로 리듀싱 | int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum)); |
collectionAndThen | 변환 함수가 형식을 변환 | 다른 컬렉터를 감싸고 그 결과에 변환 함수를 적용 | int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size)); |
groupingBy | Map<K, List |
하나의 프로퍼티값을 기준으로 스트림의 항목을 그룹화하며 기준 프로퍼티값을 결과 맵의 키로 사용 | Map<Dish.Type, List |
partitionBy | Map<Boolean, List |
프레디케이트를 스트림의 각 항복에 적용한 결과로 항목을 분할 | Map<Boolean, List |
Collector 인터페이스
Collector 인터페이스 살펴 보기
1 | public interface Collector<T, A, R> { |
- T는 수집될 스트림 항목의 제네릭 형식이다.
- A는 누적자, 즉 수집 과정에서 중간 결과를 누적하는 객체의 형식이다.
- R은 수집 연산 결과 객체의 형식(항상 그런 것은 아니지만 대게 컬렉션 형식)이다.
예를 들어 Stream<T>
의 모든 요소를 List<T>
로 수집하는 ToListCollector<T>
라는 클래스를 구현할 수 있다.
1 | public class ToListCollector<T> implements Collector<T, List<T>, List<T>> |
Collector 인터페이스 메서드 살펴보기
supplier 메서드: 새로운 결과 컨테이너 만들기
suplier 메서드는 빈 결과로 이루어진 Supplier를 반환해야 한다. 즉 supplier는 수집과정에서 빈 누적자 인스턴스를 만드는 파라미터가 없는 함수다.
1 | public Supplier<List<T>> supplier() { |
accumulator 메서드: 결과 컨테이너에 요소 추가하기
accumulator 메서드는 리듀싱 연산을 수행하는 함수를 반환한다. 스트림의 n번째 연산을 탐색할 때 두 인수, 즉 누적자(스트림의 첫 n-1개 항목을 수집한 상태)와 n번째 요소를 함수에 적용한다.
1 | public BiConsumer<List<T>, T> accumulator() { |
finisher 메서드: 최종 변환값을 결과 컨테이너로 적용하기
finisher 메서드는 스트림 탐색을 끝내고 누적자 객체를 최종 결과로 반환하면서 누적 과정을 끝낼 때 호출할 함수를 반환해야 한다.
1 | public Function<List<T>, List<T>> finisher() { |
combiner 메서드: 두 결과 컨테이너 병합
combiner는 스트림의 서로 다른 서브파트를 병렬로 처리할 때 누적자가 이 결과를 어떻게 처리할지 정의한다.
1 | public BinaryOperator<List<T>> combiner() { |
Characteristics 메서드
characteristics 메서드는 컬렉터의 연산을 정의하는 Characteristics 형식의 불변 집합을 반환한다.
- UNORDERED 이듀싱 결과는 스트림 요소의 방문 순서나 누적 순서에 영향을 받지 않는다.
- CONCURRENT 다중 스레드에서 accumulator 함수를 동시에 호출할 수 있으며 이 컬렉터는 스트림의 병렬 리듀싱을 수행할 수 있다.
- IDENTITIY_FINISH finisher 메서드가 반환하는 함수는 단순히 identity를 적용할 뿐이므로 이를 생략할 수 있다.
1 | public Set<Characteristics> characteristics() { |