사용자 정의 수집기 만들기
Collector 인터페이스를 구현해 특정 도메인 용도에 재사용할 수 있는 집계 기능을 만듭니다.
사용자 정의 수집기 만들기은(는) 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.0Collector 인터페이스 직접 구현하기
더 세밀하게 제어하려면 클래스로 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 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.