0Pricing
Java Academy · Lektion

Einen eigenen Collector erstellen

Implementieren Sie die Collector-Schnittstelle, um eine wiederverwendbare Aggregation für einen domänenspezifischen Anwendungsfall zu erstellen.

Einen eigenen Collector erstellen ist eine kostenlose Java Academy-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Java Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Java Academy-Kurs umfasst insgesamt 4 Lektionen.

Warum einen eigenen Collector erstellen?

Wenn die integrierten Collector nicht zu Ihrer Domäne passen, implementieren Sie die Schnittstelle Collector<T, A, R>, um wiederverwendbare und kombinierbare Aggregationslogik zu erstellen. T=Eingabe, A=veränderlicher Akkumulator, R=Ergebnis.

Die Collector-Schnittstelle

Ein Collector besteht aus fünf Komponenten:

  • supplier() – erstellt den initialen Akkumulator
  • accumulator() – fügt ein Element in den Akkumulator ein
  • combiner() – führt zwei Akkumulatoren zusammen (für parallele Streams)
  • finisher() – wandelt den Akkumulator in das endgültige Ergebnis um
  • characteristics() – Hinweise zur Optimierung

Die Factory Collector.of()

Der einfachste Weg, einen eigenen Collector zu erstellen, ist Collector.of(), ohne die Schnittstelle direkt zu implementieren:

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

Eigener Collector: TopN

Erstellen Sie einen Collector, der anhand eines Comparators nur die besten N Elemente behält:

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-Collector verwenden

Wenden Sie den eigenen TopN-Collector auf einen Stream an:

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

Merkmalskennzeichnungen

Drei optionale Merkmale dienen der Optimierung von Streams:

  • IDENTITY_FINISH – der Finisher ist eine Identitätsfunktion und kann übersprungen werden
  • UNORDERED – die Reihenfolge des Ergebnisses ist unerheblich
  • CONCURRENT – der Akkumulator kann gleichzeitig aufgerufen werden

Das Setzen des falschen Merkmals, etwa CONCURRENT ohne Thread-Sicherheit, führt zu Fehlern.

Eigener Collector: Laufender Durchschnitt

Halten Sie einen laufenden Durchschnitt ohne einen zweiten Durchlauf aufrecht:

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-Schnittstelle direkt implementieren

Für mehr Kontrolle implementieren Sie Collector<T,A,R> als Klasse:

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(); }
}

Eigene und integrierte Collector vergleichen

Prüfen Sie vor dem Schreiben eines eigenen Collectors, ob eine Kombination integrierter Collector dasselbe Ergebnis liefert – sie sind bereits getestet und optimiert:

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

Thread-Sicherheit in eigenen Collectors

Wenn das Merkmal CONCURRENT gesetzt ist, wird der Akkumulator gleichzeitig von mehreren Threads aufgerufen. Der Akkumulator muss dann thread-sicher sein, etwa durch die Verwendung von ConcurrentHashMap oder AtomicLong. Ohne CONCURRENT übernimmt der Combiner die Parallelisierung sicher.

Praxisbeispiel: Histogramm-Collector

Ordnen Sie numerische Werte in einer Histogramm-Map Klassen zu:

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));

Kurze Überprüfung

Welches Merkmal von Collector.Characteristics zeigt an, dass die Finisher-Funktion die Identitätstransformation ist und übersprungen werden kann?

Rückblick: Eigene Collector

Die wichtigsten Punkte:

  • Collector<T,A,R>: T=Eingabe, A=Akkumulator, R=Ergebnis
  • Verwenden Sie Collector.of() für eine schnelle Implementierung ohne eigene Klasse
  • supplier/accumulator/combiner/finisher/characteristics sind die fünf Bestandteile
  • IDENTITY_FINISH, UNORDERED und CONCURRENT sind Optimierungsmerkmale
  • Bevorzugen Sie die Kombination integrierter Collector und erstellen Sie nur bei Bedarf eigene

Häufig gestellte Fragen

Ist die Lektion „Einen eigenen Collector erstellen“ kostenlos?

Ja — der vollständige Text von „Einen eigenen Collector erstellen“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Java Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Java Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Einen eigenen Collector erstellen“?

Implementieren Sie die Collector-Schnittstelle, um eine wiederverwendbare Aggregation für einen domänenspezifischen Anwendungsfall zu erstellen. Du übst Java Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Java Academy zu starten?

Keine Vorkenntnisse erforderlich. Java Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.

Wie lange dauert die Lektion „Einen eigenen Collector erstellen“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Java Academy-Lektion Code schreiben und ausführen?

Ja. Jede Java Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. groupingBy: Elemente klassifizieren
  2. partitioningBy und counting
  3. toMap, joining und summarizing
  4. Einen eigenen Collector erstellen
← Zurück zu Java Academy