ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Stream(스트림) - 7. Collector 구현 (최종연산 3/3)
    Java 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

     

    반응형

    댓글

Designed by Tistory.