Java 8 Stream: groupingBy mit mehreren Collectors

8

Ich möchte einen Java-8-Stream und einen Group-by-One-Klassifikator verwenden, aber mehrere Collector-Funktionen haben. Bei der Gruppierung wird beispielsweise der Durchschnitt und die Summe eines Feldes (oder eines anderen Feldes) berechnet.

Ich versuche das mit einem Beispiel etwas zu vereinfachen:

%Vor%

Das Ergebnis sollte eine Map sein, die das Gruppierungsergebnis wie

assoziiert %Vor%

Das funktioniert perfekt mit einer Funktion wie "Collectors.counting ()", aber ich mag es, mehr als eine zu verketten (idealerweise unendlich von einer Liste).

%Vor%

Ist es möglich, so etwas zu tun?

    
PhilippS 18.08.2015, 11:54
quelle

4 Antworten

12

Für das konkrete Problem der Summierung und Mittelung verwenden Sie collectingAndThen zusammen mit summarizingDouble :

%Vor%

Für das allgemeinere Problem (sammeln Sie verschiedene Dinge über Ihre Personen), können Sie einen komplexen Kollektor wie folgt erstellen:

%Vor%

Map-Werte sind Listen, in denen das erste Element das Ergebnis der Anwendung des ersten Collectors usw. ist. Sie können einen benutzerdefinierten Finisher-Schritt mit Collectors.collectingAndThen(complexCollector, list -> ...) hinzufügen, um diese Liste in etwas geeigneteres zu konvertieren.

    
Tagir Valeev 18.08.2015, 12:17
quelle
4

Wenn Sie eine Karte als Ausgabetyp verwenden, könnte eine potenziell unendliche Liste von Reduzierern erstellt werden, von denen jede eine eigene Statistik erstellt und sie zur Karte hinzufügt.

%Vor%

...

%Vor%

Produziert:

%Vor%     
WillShackleford 18.08.2015 12:52
quelle
3

Anstatt die Kollektoren zu verketten, sollten Sie eine Abstraktion erstellen, die ein Aggregator von Kollektoren ist: Implementieren Sie die Collector -Schnittstelle mit einer Klasse, die eine Liste von Sammlern akzeptiert und jeden Methodenaufruf an sie delegiert. Am Ende geben Sie new Data() mit allen Ergebnissen zurück, die von den verschachtelten Kollektoren erzeugt wurden.

Sie können vermeiden, eine benutzerdefinierte Klasse mit allen Methodendeklarationen zu erstellen, indem Sie Collector.of(supplier, accumulator, combiner, finisher, Collector.Characteristics... characteristics) verwenden. finisher lambda ruft den Finisher jedes verschachtelten Kollektors auf und gibt dann die Data -Instanz zurück.

    
Marko Topolnik 18.08.2015 12:31
quelle
3

Sie könnten sie verketten,

Ein Kollektor kann nur ein Objekt erzeugen, aber dieses Objekt kann mehrere Werte enthalten. Sie können beispielsweise eine Map zurückgeben, wenn die Map für jeden Collector, den Sie zurückgeben, einen Eintrag enthält.

Sie können Collectors.of(HashMap::new, accumulator, combiner);

verwenden

Ihr accumulator würde eine Map of Collectors haben, in der die Schlüssel der erzeugten Map mit dem Namen des Collectors übereinstimmen. Te Combiner würde eine Möglichkeit benötigen, mehrere Ergebnisse zu kombinieren, wenn dies parallel ausgeführt wird.

Im Allgemeinen verwenden die eingebauten Kollektoren einen Datentyp für komplexe Ergebnisse.

Von Sammlern

%Vor%

und in einer eigenen Klasse

%Vor%     
Peter Lawrey 18.08.2015 12:00
quelle

Tags und Links