Java Academy · Pelajaran

Membina Pengumpul Tersuai

Laksanakan antara muka Collector untuk mencipta pengagregatan boleh guna semula bagi kes penggunaan khusus domain.

Pelajaran 4 daripada 413 langkah

Membina Pengumpul Tersuai ialah pelajaran Java Academy percuma di CoddyKit. Ini ialah pelajaran 4 daripada 4. Anda boleh membaca keseluruhan pelajaran di bawah secara percuma — kemudian berlatih secara praktikal dalam pelayar menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7. Pelajaran ini merupakan sebahagian daripada laluan pembelajaran Java Academy, dan kemajuan anda disegerakkan merentas web serta aplikasi CoddyKit. Kursus Java Academy merangkumi sejumlah 4 pelajaran.

Mengapa Membina Pengumpul Tersuai?

Apabila pengumpul terbina dalam tidak sesuai dengan domain anda, laksanakan antara muka Collector<T, A, R> untuk mencipta logik pengagregatan yang boleh diguna semula dan digabungkan. T=masukan, A=pengumpul boleh ubah, R=hasil.

Antara Muka Pengumpul

Pengumpul mempunyai lima komponen:

  • supplier() — mencipta pengumpul awal
  • accumulator() — memasukkan satu elemen ke dalam pengumpul
  • combiner() — menggabungkan dua pengumpul untuk strim selari
  • finisher() — mengubah pengumpul kepada hasil akhir
  • characteristics() — petunjuk pengoptimuman

Kilang Collector.of()

Cara paling mudah untuk mencipta pengumpul tersuai ialah menggunakan Collector.of() tanpa melaksanakan antara muka 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,Carol

Pengumpul Tersuai: N Teratas

Bina pengumpul yang hanya mengekalkan N elemen teratas berdasarkan pembanding:

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 Pengumpul N Teratas

Gunakan pengumpul N Teratas tersuai pada strim:

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

Bendera Ciri

Tiga ciri pilihan melaraskan pengoptimuman strim:

  • IDENTITY_FINISH — penamat ialah identiti; langkau langkah ini
  • UNORDERED — susunan hasil tidak penting
  • CONCURRENT — pengumpul boleh dipanggil secara serentak

Menetapkan bendera yang salah, contohnya CONCURRENT tanpa keselamatan benang, menyebabkan pepijat.

Pengumpul Tersuai: Purata Berjalan

Kekalkan purata 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.0

Melaksanakan Antara Muka Pengumpul Secara Langsung

Untuk kawalan yang lebih besar, laksanakan Collector<T,A,R> sebagai 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 Pengumpul Tersuai dengan Pengumpul Terbina Dalam

Sebelum menulis pengumpul tersuai, semak sama ada gabungan pengumpul terbina dalam dapat mencapai hasil yang sama — pengumpul tersebut telah diuji dan dioptimumkan:

// 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

Keselamatan Benang dalam Pengumpul Tersuai

Jika ciri CONCURRENT ditetapkan, pengumpul dipanggil oleh beberapa benang secara serentak. Oleh itu, pengumpul mestilah selamat untuk benang, contohnya menggunakan ConcurrentHashMap atau AtomicLong. Tanpa CONCURRENT, penggabung mengendalikan pemprosesan selari dengan selamat.

Kes Penggunaan Sebenar: Pengumpul Histogram

Kelompokkan nilai berangka ke dalam peta 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));

Semakan Ringkas

Apakah bendera ciri pengumpul yang menunjukkan bahawa fungsi penamat ialah transformasi identiti dan boleh dilangkau?

Ringkasan: Pengumpul Tersuai

Perkara utama:

  • Pengumpul<T,A,R>: T=masukan, A=pengumpul, R=hasil
  • Gunakan Collector.of() untuk pelaksanaan pantas tanpa kelas
  • pembekal/pengumpul/penggabung/penamat/ciri ialah lima bahagian
  • Bendera pengoptimuman: penamat identiti, UNORDERED dan CONCURRENT
  • Utamakan gabungan pengumpul terbina dalam; gunakan pengumpul tersuai hanya apabila perlu
Percuma untuk bermula

Pelajari Java dengan tutor kecerdasan buatan — percuma

Tulis dan jalankan kod sebenar dalam pelayar anda, dapatkan bantuan segera daripada tutor kecerdasan buatan yang tersedia 24/7, dan sambung semula dari tempat anda berhenti di web atau dalam aplikasi.

Kursus
104
Pelajaran
374

Soalan Lazim

Adakah pelajaran “Membina Pengumpul Tersuai” percuma?

Ya — teks penuh “Membina Pengumpul Tersuai” boleh dibaca secara percuma di web ini. Untuk berlatih secara interaktif menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7, serta membuka kunci baki kursus Java Academy, tingkat taraf kepada CoddyKit PRO. Kursus Java Academy merangkumi sejumlah 4 pelajaran.

Apakah yang akan saya pelajari dalam “Membina Pengumpul Tersuai”?

Laksanakan antara muka Collector untuk mencipta pengagregatan boleh guna semula bagi kes penggunaan khusus domain. Anda berlatih Java Academy menggunakan kod praktikal yang dijalankan terus dalam pelayar, manakala tutor kecerdasan buatan 24/7 menjawab soalan anda semasa anda mengikuti pelajaran.

Adakah saya memerlukan pengalaman untuk memulakan Java Academy?

Tiada pengalaman terdahulu diperlukan. Pembelajaran Java Academy di CoddyKit disusun untuk pelajar daripada peringkat pemula hingga lanjutan, jadi anda boleh bermula di sini atau dari awal dan belajar mengikut kadar anda sendiri. Ini ialah pelajaran 4 daripada 4.

Berapa lamakah pelajaran “Membina Pengumpul Tersuai” diambil?

Kebanyakan pelajaran CoddyKit mengambil masa kira-kira 5–10 minit. Setiap pelajaran ringkas dan interaktif, jadi anda boleh membuat kemajuan secara berterusan dan menyambung tepat dari tempat anda berhenti di web atau aplikasi.

Bolehkah saya menulis dan menjalankan kod dalam pelajaran Java Academy ini?

Ya. Setiap pelajaran Java Academy menyertakan penyunting kod terbina dalam, jadi anda boleh menulis dan menjalankan kod sebenar terus dalam pelayar serta menerima maklum balas kecerdasan buatan serta-merta — tanpa memerlukan persediaan setempat.

Semua pelajaran dalam kursus ini

  1. groupingBy: Mengelaskan Elemen
  2. partitioningBy dan counting
  3. toMap, joining dan summarizing
  4. Membina Pengumpul Tersuai
← Kembali ke Java Academy