java.util.stream.Collectors
를 사용하게 된다.
https://junhyunny.blogspot.com/2019/04/collectors-reducing.html
https://junhyunny.blogspot.com/2019/04/stream-collect.html
https://junhyunny.blogspot.com/2019/04/collectors-joining.html
.collect(groupingBy(Function classifier, (Supplier mapFactory), (Collector downstream)))
위와 같은 형태를 갖는다.
https://junhyunny.blogspot.com/2019/04/collectors-groupingby.html
public final class Collectors {
// 인수 1개를 갖는 경우: classifier(분류자)를 넣는다.
// 인수를 두개 갖는 메서드를 리턴함
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier) {
return groupingBy(classifier, toList());
}
// 인수 2개를 갖는 경우:
public static <T, K, A, D>
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream) {
return groupingBy(classifier, HashMap::new, downstream);
}
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream) {
Supplier<A> downstreamSupplier = downstream.supplier();
BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
BiConsumer<Map<K, A>, T> accumulator = (m, t) -> {
K key = Objects.requireNonNull(classifier.apply(t),
"element cannot be mapped to a null key");
A container = m.computeIfAbsent(key, k -> downstreamSupplier.get());
downstreamAccumulator.accept(container, t);
};
BinaryOperator<Map<K, A>> merger = Collectors.<K, A, Map<K, A>>mapMerger(
downstream.combiner());
@SuppressWarnings("unchecked")
Supplier<Map<K, A>> mangledFactory = (Supplier<Map<K, A>>) mapFactory;
if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {
return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_ID);
} else {
@SuppressWarnings("unchecked")
Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher();
Function<Map<K, A>, M> finisher = intermediate -> {
intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));
@SuppressWarnings("unchecked")
M castResult = (M) intermediate;
return castResult;
};
return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID);
}
}
}
.collect(partitioningBy(Predicate predicate, (Collector downstream)))
위와 같은 형태를 갖는다.
https://junhyunny.blogspot.com/2019/04/collectors-partitioningby.html
public final class Collectors {
public static <T>
Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
return partitioningBy(predicate, toList());
}
public static <T, D, A>
Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
Collector<? super T, A, D> downstream) {
BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
BiConsumer<Partition<A>, T> accumulator = (result, t) ->
downstreamAccumulator.accept(predicate.test(t) ? result.forTrue : result.forFalse, t);
BinaryOperator<A> op = downstream.combiner();
BinaryOperator<Partition<A>> merger = (left, right) ->
new Partition<>(op.apply(left.forTrue, right.forTrue),
op.apply(left.forFalse, right.forFalse));
Supplier<Partition<A>> supplier = () ->
new Partition<>(downstream.supplier().get(),
downstream.supplier().get());
if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {
return new CollectorImpl<>(supplier, accumulator, merger, CH_ID);
} else {
Function<Partition<A>, Map<Boolean, D>> finisher = par ->
new Partition<>(downstream.finisher().apply(par.forTrue),
downstream.finisher().apply(par.forFalse));
return new CollectorImpl<>(supplier, accumulator, merger, finisher, CH_NOID);
}
}
}
public interface Collector<T, A, R> {
// 새로운 결과 컨테이너 만들기
Supplier<A> supplier();
// 결과 컨테이너에 요소 추가하기
BiConsumer<A, T> accumulator();
// 최종 변환값을 결과 컨테이너로 적용하기
Function<A, R> finisher();
// 두 결과 컨테이너 병합
BinaryOperator<A> combiner();
// 스트림을 병렬로 리듀스할 것인지
// 병렬로 리듀스 한다면 어떤 최적화를 선택할지 힌트 제공
Set<Characteristics> characteristics();
}
toList
구현public class ToListCollector<T> implements Collector<T, List<T>, List<T>> {
@Override
public Supplier<List<T>> supplier() {
// 수집 연산
return ArrayList::new;
}
@Override
public BiConsumer<List<T>, T> accumulator() {
// 탐색한 항목 누적, 누적자를 고친다
return List::add;
}
@Override
public Function<List<T>, List<T>> finisher() {
// 항등 함수
return Function.identity();
}
@Override
public BinaryOperator<List<T>> combiner() {
// 두번째 콘텐츠와 합쳐서 첫 번째 누적자를 고친다
return (list1, list2) -> {
// 변경된 첫 번째 누적자를 반환한다
list1.addAll(list2);
return list1;
};
}
@Override
public Set<Characteristics> characteristics() {
// 컬렉터의 플래그를 IDENTITY_FINISH와 CONCURRENT로 설정한다
return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH, CONCURRENT));
}
}
public class PartitionPrimeNumbers {
// 커스텀 컬렉터 구현
// 1단계: Collector 클래스 시그니처 정의
public static class PrimeNumbersCollector implements
Collector<Integer // 스트림 요소의 형식
, Map<Boolean, List<Integer>> // 누적자 형식
, Map<Boolean, List<Integer>>> { // 수집 연산의 결과 형식
// 2단계: 리듀싱 연산 구현
// supplier 메서드는 누적자를 만드는 함수를 반환
@Override
public Supplier<Map<Boolean, List<Integer>>> supplier() {
// 두 개의 빈 리스트를 포함하는 맵으로 수집 동작 시작
return () -> new HashMap<Boolean, List<Integer>>() {{
put(true, new ArrayList<>()); // 소수가 추가될 빈 리스트로 초기화
put(false, new ArrayList<>()); // 비소수가 추가될 빈 리스트로 초기화
}};
}
// 스트림의 요소를 어떻게 수집할지 결정하는 accumulator 메서드
@Override
public BiConsumer<Map<Boolean, List<Integer>>, Integer> accumulator() {
return (Map<Boolean, List<Integer>> acc, Integer candidate) -> {
// isPrime의 결과에 따라 소수 리스트와 비소수 리스트를 만든다
// 지금까지 발견한 소수 리스트를 isPrime 메서드로 전달한다
acc.get(isPrime(acc.get(true), candidate))
// candidate를 알맞은 리스트에 추가한다
// isPrime 메서드의 결과에 따라 맵에서 알맞은 리스트를 받아 현재 candidate를 추가한다
.add(candidate);
};
}
// 3단계: 병렬 실행할 수 있는 컬렉터 만들기(as possible)
@Override
public BinaryOperator<Map<Boolean, List<Integer>>> combiner() {
// 두 번째 맵을 첫번째 맵에 병합한다
return (Map<Boolean, List<Integer>> map1, Map<Boolean, List<Integer>> map2) -> {
map1.get(true).addAll(map2.get(true));
map1.get(false).addAll(map2.get(false));
return map1;
};
}
// 4단계: finisher 메서드와 컬렉터의 characteristics 메서드
// accumulator의 형식은 컬렉터 결과 형식과 같으므로 반환 과정이 필요 없다.
@Override
public Function<Map<Boolean, List<Integer>>, Map<Boolean, List<Integer>>> finisher() {
return Function.identity(); // 항등 함수
}
// 커스텀 컬렉터는 CONCURRENT도 아니고 UNORDERED도 아니지만 IDENTITY_FINISH
@Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH));
}
}
}