Java Academy · 강의

사용자 정의 수집기 만들기

Collector 인터페이스를 구현해 특정 도메인 용도에 재사용할 수 있는 집계 기능을 만듭니다.

레슨 4/413개 단계

사용자 정의 수집기 만들기은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

사용자 지정 수집기를 만드는 이유

내장 수집기가 해당 분야에 맞지 않을 때는 Collector<T, A, R> 인터페이스를 구현하여 재사용 가능하고 조합 가능한 집계 로직을 만듭니다. T=입력, A=변경 가능한 누적기, R=결과입니다.

Collector 인터페이스

Collector에는 네 가지 구성 요소가 있습니다:

  • supplier() — 초기 누적기를 생성합니다
  • accumulator() — 하나의 요소를 누적기에 반영합니다
  • combiner() — 두 누적기를 병합합니다(병렬 stream에서 사용)
  • finisher() — 누적기를 최종 결과로 변환합니다
  • characteristics() — 최적화를 위한 힌트입니다

Collector.of() 팩터리

사용자 지정 수집기를 만드는 가장 쉬운 방법은 인터페이스를 직접 구현하지 않고 Collector.of()를 사용하는 것입니다:

import java.util.stream.*;

// Collector that builds a comma-separated String
Collector<String, StringBuilder, String> csvCollector = Collector.of(
    StringBuilder::new,                     // supplier
    (sb, s) -> {
        if (sb.length() > 0) sb.append(',');
        sb.append(s);
    },                                       // accumulator
    (a, b) -> {
        if (a.length() > 0 && b.length() > 0) a.append(',');
        a.append(b); return a;
    },                                       // combiner
    StringBuilder::toString                  // finisher
);

String csv = Stream.of("Alice","Bob","Carol").collect(csvCollector);
System.out.println(csv); // Alice,Bob,Carol

사용자 지정 수집기: TopN

Comparator에 따라 상위 N개 요소만 유지하는 수집기를 만듭니다:

import java.util.*;
import java.util.stream.*;

static <T> Collector<T, ?, List<T>> topN(int n, Comparator<T> comp) {
    return Collector.of(
        () -> new ArrayList<T>(),
        (list, e) -> {
            list.add(e);
            list.sort(comp.reversed());
            if (list.size() > n) list.remove(list.size() - 1);
        },
        (a, b) -> {
            a.addAll(b);
            a.sort(comp.reversed());
            while (a.size() > n) a.remove(a.size() - 1);
            return a;
        },
        Collector.Characteristics.UNORDERED
    );
}

TopN 수집기 사용하기

stream에서 사용자 지정 TopN 수집기를 적용합니다:

List<Integer> top3 = Stream.of(5,2,9,1,7,3,8)
    .collect(topN(3, Comparator.naturalOrder()));
System.out.println(top3); // [9, 8, 7]

특성 플래그

세 가지 선택적 특성으로 stream 최적화를 조정할 수 있습니다:

  • IDENTITY_FINISH — finisher가 항등 함수이므로 생략합니다
  • UNORDERED — 결과 순서가 중요하지 않습니다
  • CONCURRENT — 누적기를 동시에 호출할 수 있습니다

잘못된 플래그를 설정하면(예: 스레드 안전성이 없는 상태에서 CONCURRENT 설정) 버그가 발생합니다.

사용자 지정 수집기: 누적 평균

두 번째 순회 없이 누적 평균을 유지합니다:

record RunningAvg(long count, double sum) {
    RunningAvg add(double v) { return new RunningAvg(count+1, sum+v); }
    RunningAvg merge(RunningAvg o) { return new RunningAvg(count+o.count, sum+o.sum); }
    double avg() { return count == 0 ? 0 : sum / count; }
}

Collector<Double, RunningAvg[], Double> avgCollector = Collector.of(
    () -> new RunningAvg[]{new RunningAvg(0, 0)},
    (a, v) -> a[0] = a[0].add(v),
    (a, b) -> { a[0] = a[0].merge(b[0]); return a; },
    a -> a[0].avg()
);

double avg = Stream.of(10.0, 20.0, 30.0).collect(avgCollector);
System.out.println(avg); // 20.0

Collector 인터페이스 직접 구현하기

더 세밀하게 제어하려면 클래스로 Collector<T,A,R>를 구현합니다:

import java.util.function.*;
import java.util.stream.*;

class StringJoiningCollector implements Collector<String, List<String>, String> {
    private final String delimiter;
    StringJoiningCollector(String delimiter) { this.delimiter = delimiter; }
    public Supplier<List<String>> supplier() { return ArrayList::new; }
    public BiConsumer<List<String>, String> accumulator() { return List::add; }
    public BinaryOperator<List<String>> combiner() {
        return (a,b) -> { a.addAll(b); return a; };
    }
    public Function<List<String>, String> finisher() {
        return list -> String.join(delimiter, list);
    }
    public Set<Characteristics> characteristics() { return Set.of(); }
}

사용자 지정 수집기와 내장 수집기 비교

사용자 지정 수집기를 작성하기 전에 내장 수집기를 조합하여 같은 결과를 얻을 수 있는지 확인하십시오. 내장 수집기는 이미 검증되고 최적화되어 있습니다:

// Instead of custom, compose:
Map<String, IntSummaryStatistics> stats = orders.stream().collect(
    Collectors.groupingBy(Order::status,
        Collectors.summarizingInt(Order::amount))
);
// This replaces many custom collector implementations

사용자 지정 수집기의 스레드 안전성

CONCURRENT 특성이 설정되면 여러 스레드에서 누적기가 동시에 호출됩니다. 따라서 누적기는 스레드 안전해야 합니다(예: ConcurrentHashMap 또는 AtomicLong 사용). CONCURRENT가 없으면 combiner가 병렬 처리를 안전하게 담당합니다.

실제 사용 사례: 히스토그램 수집기

숫자 값을 히스토그램 맵의 구간에 넣습니다:

Collector<Integer, Map<Integer, Long>, Map<Integer, Long>> histogram =
    Collector.of(
        LinkedHashMap::new,
        (map, val) -> map.merge(val / 10 * 10, 1L, Long::sum),
        (a, b) -> { b.forEach((k,v) -> a.merge(k,v,Long::sum)); return a; },
        Collector.Characteristics.IDENTITY_FINISH
    );

Map<Integer,Long> hist = Stream.of(5,15,25,12,8,22,18,3)
    .collect(histogram);
hist.forEach((bucket, cnt) -> System.out.println(bucket+"-"+(bucket+9)+": "+cnt));

빠른 확인

finisher 함수가 항등 변환이므로 생략할 수 있음을 나타내는 Collector.Characteristics 플래그는 무엇입니까?

복습: 사용자 지정 수집기

핵심 내용:

  • Collector<T,A,R>: T=입력, A=누적기, R=결과
  • 클래스 없이 빠르게 구현하려면 Collector.of()를 사용합니다
  • supplier/accumulator/combiner/finisher/characteristics가 5가지 구성 요소입니다
  • IDENTITY_FINISH, UNORDERED, CONCURRENT는 최적화 플래그입니다
  • 내장 수집기를 조합하는 방식을 우선하고, 필요한 경우에만 사용자 지정 수집기를 사용합니다
무료로 시작

AI 튜터와 함께 Java을(를) 배우세요 — 무료

브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.

코스
104
레슨
374

자주 묻는 질문

“사용자 정의 수집기 만들기” 강의는 무료인가요?

네 — “사용자 정의 수집기 만들기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“사용자 정의 수집기 만들기”에서 뭘 배우나요?

Collector 인터페이스를 구현해 특정 도메인 용도에 재사용할 수 있는 집계 기능을 만듭니다. 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Java Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“사용자 정의 수집기 만들기” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. groupingBy: 요소 분류
  2. partitioningBy와 counting
  3. toMap, joining, summarizing
  4. 사용자 정의 수집기 만들기
← Java Academy(으)로 돌아가기