Membangun Collector Kustom
Implementasikan interface Collector untuk membuat agregasi yang dapat digunakan kembali dalam kebutuhan khusus domain.
Membangun Collector Kustom adalah pelajaran Java Academy gratis di CoddyKit. Ini adalah pelajaran 4 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Java Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Java Academy mencakup 4 pelajaran total.
Mengapa Membuat Kolektor Khusus?
Jika kolektor bawaan tidak sesuai dengan domain Anda, implementasikan antarmuka Collector<T, A, R> untuk membuat logika agregasi yang dapat digunakan kembali dan dikomposisikan. T=input, A=akumulator yang dapat diubah, R=hasil.
Antarmuka Kolektor
Sebuah kolektor memiliki lima komponen:
supplier()— membuat akumulator awalaccumulator()— memasukkan satu elemen ke dalam akumulatorcombiner()— menggabungkan dua akumulator (untuk stream paralel)finisher()— mengubah akumulator menjadi hasil akhircharacteristics()— petunjuk untuk optimasi
Pabrik Collector.of()
Cara termudah untuk membuat kolektor khusus adalah Collector.of(), tanpa mengimplementasikan antarmukanya secara langsung:
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,CarolKolektor Khusus: TopN
Buat kolektor yang hanya mempertahankan N elemen teratas berdasarkan Comparator:
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
);
}Menggunakan Kolektor TopN
Terapkan kolektor khusus TopN pada sebuah stream:
List<Integer> top3 = Stream.of(5,2,9,1,7,3,8)
.collect(topN(3, Comparator.naturalOrder()));
System.out.println(top3); // [9, 8, 7]Tanda Karakteristik
Tiga karakteristik opsional menyetel optimasi stream:
IDENTITY_FINISH— finisher adalah identitas; lewati finisherUNORDERED— urutan hasil tidak pentingCONCURRENT— akumulator dapat dipanggil secara bersamaan
Menyetel tanda yang salah (misalnya, CONCURRENT tanpa keamanan thread) menyebabkan bug.
Kolektor Khusus: Rata-rata Berjalan
Pertahankan rata-rata berjalan tanpa lintasan kedua:
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.0Mengimplementasikan Antarmuka Kolektor Secara Langsung
Untuk mendapatkan kontrol lebih besar, implementasikan Collector<T,A,R> sebagai sebuah kelas:
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(); }
}Membandingkan Kolektor Khusus dan Bawaan
Sebelum menulis kolektor khusus, periksa apakah komposisi kolektor bawaan dapat menghasilkan hasil yang sama — kolektor tersebut sudah diuji dan dioptimalkan:
// Instead of custom, compose:
Map<String, IntSummaryStatistics> stats = orders.stream().collect(
Collectors.groupingBy(Order::status,
Collectors.summarizingInt(Order::amount))
);
// This replaces many custom collector implementationsKeamanan Thread pada Kolektor Khusus
Jika karakteristik CONCURRENT ditetapkan, akumulator dipanggil oleh beberapa thread secara bersamaan. Akumulator tersebut harus aman terhadap thread (misalnya, gunakan ConcurrentHashMap atau AtomicLong). Tanpa CONCURRENT, combiner menangani pemrosesan paralel dengan aman.
Kasus Nyata: Kolektor Histogram
Kelompokkan nilai numerik ke dalam map histogram:
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));Pemeriksaan Singkat
Tanda Collector.Characteristics mana yang menunjukkan bahwa fungsi finisher merupakan transformasi identitas dan dapat dilewati?
Ringkasan: Kolektor Khusus
Poin-poin penting:
- Collector<T,A,R>: T=input, A=akumulator, R=hasil
- Gunakan Collector.of() untuk implementasi cepat tanpa sebuah kelas
- supplier/accumulator/combiner/finisher/characteristics adalah lima bagiannya
- IDENTITY_FINISH, UNORDERED, CONCURRENT adalah tanda optimasi
- Utamakan komposisi kolektor bawaan; gunakan kolektor khusus hanya jika diperlukan
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Membangun Collector Kustom” gratis?
Ya — teks lengkap “Membangun Collector Kustom” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Java Academy, upgrade ke CoddyKit PRO. Kursus Java Academy mencakup 4 pelajaran total.
Apa yang akan aku pelajari di “Membangun Collector Kustom”?
Implementasikan interface Collector untuk membuat agregasi yang dapat digunakan kembali dalam kebutuhan khusus domain. Kamu berlatih Java Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai Java Academy?
Tidak diperlukan pengalaman sebelumnya. Java Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 4 dari 4.
Berapa lama pelajaran “Membangun Collector Kustom” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran Java Academy ini?
Ya. Setiap pelajaran Java Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- groupingBy: Mengelompokkan Elemen
- partitioningBy dan counting
- toMap, joining, dan summarizing
- Membangun Collector Kustom