Java
Stream(스트림) - 7. Collector 구현 (최종연산 3/3)
jamie.
2020. 3. 11. 22:14
반응형
Collector 작성
Collector인터페이스를 구현
Collector Interface
직접 구현해야 하는 5개의 메서드
public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
BinaryOperator<A> combiner();
Function<A, R> finisher();
Set<Characteristics> characteristics(); // 컬렉터의 특성이 담긴 Set 반환
...
}
characteristics()를 제외하면 모두 반환타입이 함수형 인터페이스 = 4개의 람다식
supplier() 작업 결과를 저장할 공간 제공
accumulator() 스트림의 요소를 수집(collect)할 방법 제공
스트림 요소를 어떻게 supplier가 제공한 공간에 누적할지 정의
combineder() 두 저장공간을 병합할 방법을 제공(병렬 스트림)
병렬 스트림인 경우, 여러 쓰레드에 의해 처리된 결과를 어떻게 합칠지 정의
finisher() 결과를 최종적으로 변환할 방법을 제공
변환이 필요없다면 항등 함수인 Function.identity()를 반환하면 됨
public Function finisher() {
return Function.identity(); // 항등 함수 반환. return x -> x;와 동일
}
characteristics()는 컬렉터가 수행하는 작업의 속성에 대한 정보를 제공하기 위한 것
아래 세가지 속성 중 해당하는 것을 Set에 담아서 반환하도록 구현하면 됨
Characteristics.CONCURRENT 병렬로 처리할 수 있는 작업
Characteristics.UNORDERED 스트림의 요소의 순서가 유지될 필요가 없는 작업
Characteristics.IDENTITY_FINISH finisher()가 항등 함수인 작업
public Set<Characteristics> charateristics() {
return Collections.unmodifiableSet(EnumSet.of(
Collector.Characteristics.CONCURRENT,
Collector.Characteristics.UNORDERED
));
}
아무런 속성도 지정하고 싶지 않은 경우
Set<Characteristics> characteristics() {
return Collections.emptySet(); // 지정할 특성이 없는 경우 비어있는 Set 반환
}
reduce() VS collect()
finisher()를 제외한 supplier(), accumulator(), combiner()는 리듀싱에서 배울 때 등장한 개념
- Collector도 내부적으로 처리하는 과정이 리듀싱과 같다는 의미
IntStream의 count(), sum(), max(), min() 등이 reduce()로 구현되어 있었음
- collect()로도 count()등의 메서드로 같은 일을 할 수 있음
long count = studentStream.count();
long count = studentStream.collect(Collectors.counting());
근본적으로 하는 일이 같음
collect() : 그룹화와 분할, 집계 등에 유용하게 쓰이고 병렬화에 있어서 reduce()보다 collect()가 더 유리함
reduce()에 대해 잘 이해한다면, Collector를 구현하는 것이 어렵지 않음
예제 - Stream<String>의 모든 문자열을 하나로 결합해서 String으로 반환하는 컬렉터
String 배열의 모든 문자열을 하나의 문자열로 합치려면?
String[] strArr = { "aaa", "bbb", "ccc" };
StringBuffer sb = new StringBuffer(); // supplier(), 저장할 공간 생성
for(String tmp : strArr) {
sb.append(tmp); // accumulator(), sb에 요소를 저장
}
String result = sb.toString(); // finisher(), StringBuffer를 String으로 변환
Stream<String>의 모든 문자열을 하나로 결합해서 String으로 반환하는 ConcatCollector
import java.util.Collections;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
public class ConcatCollector implements Collector<String, StringBuilder, String> {
@Override
public Supplier<StringBuilder> supplier() {
return StringBuilder::new;
// return () -> new StringBuilder();
}
@Override
public BiConsumer<StringBuilder, String> accumulator() {
return StringBuilder::append;
// return (sb, s) -> sb.append(s);
}
@Override
public BinaryOperator<StringBuilder> combiner() {
return StringBuilder::append;
// return (sb, sb2) -> sb.append(sb2);
}
@Override
public Function<StringBuilder, String> finisher() {
return StringBuilder::toString;
// return sb -> sb.toString();
}
@Override
public Set<Characteristics> characteristics() {
return Collections.emptySet();
}
}
만든 Collector를 사용하는 클래스
import java.util.Arrays;
import java.util.stream.Stream;
public class CollectorUse {
public static void main(String[] args) {
String[] strings = { "Jamie", "JAMIE", "jamie" };
Stream<String> stringStream = Stream.of(strings);
String result = stringStream.collect(new ConcatCollector());
System.out.println(Arrays.toString(strings));
System.out.println("result = " + result);
}
}
[Jamie, JAMIE, jamie]
result = JamieJAMIEjamie
반응형