Gids voor Java 8 groupingBy Collector

1. Inleiding

In deze tutorial we zullen zien hoe de groupingBy verzamelaar werkt aan de hand van verschillende voorbeelden.

Om het materiaal dat in deze tutorial wordt behandeld te begrijpen, hebben we basiskennis van Java 8-functies nodig. We kunnen de inleiding tot Java 8 Streams en de gids voor Java 8's Collectors voor deze basisprincipes bekijken.

2. groupingBy Verzamelaars

De Java 8 Stroom API stelt ons in staat om gegevensverzamelingen op een declaratieve manier te verwerken.

De statische fabrieksmethoden Collectors.groupingBy () en Collectors.groupingByConcurrent () bieden ons functionaliteit die vergelijkbaar is met de ‘GROEP OP ' clausule in de SQL-taal. We gebruiken ze voor het groeperen van objecten op basis van een eigenschap en het opslaan van resultaten in een Kaart voorbeeld.

De overbelaste methoden van groupingBy zijn:

  • Ten eerste met een classificatiefunctie als methodeparameter:

statische Collector<>> groupingBy (Functieclassificatie)
  • Ten tweede met een classificatiefunctie en een tweede collector als methodeparameters:

statische Collector groupingBy (Functieclassificatie, Collector stroomafwaarts)
  • Ten slotte met een classificatiefunctie, een leveranciersmethode (die de Kaart implementatie die het eindresultaat bevat), en een tweede collector als methodeparameters:

statisch  Collector groupingBy (Function classifier, Supplier mapFactory, Collector downstream)

2.1. Voorbeeld van code-instellingen

Om het gebruik van groupingBy (), laten we een Blogpost class (we zullen een stream van Blogpost voorwerpen):

class BlogPost {String titel; String auteur; BlogPostType type; int likes; } 

Vervolgens de BlogPostType:

enum BlogPostType {NEWS, REVIEW, GUIDE} 

Dan de Lijst van Blogpost voorwerpen:

Lijst berichten = Arrays.asList (...);

Laten we ook een Tuple klasse die zal worden gebruikt om berichten te groeperen door de combinatie van hun type en schrijver attributen:

klasse Tuple {BlogPostType type; String auteur; } 

2.2. Eenvoudig groeperen op basis van één kolom

Laten we beginnen met de eenvoudigste groupingBy methode, die alleen een classificatiefunctie als parameter heeft. Op elk element van de stream wordt een classificatiefunctie toegepast. We gebruiken de waarde die door de functie wordt geretourneerd als een sleutel voor de kaart die we krijgen van de groupingBy verzamelaar.

Om de blogposts in de lijst met blogposts te groeperen op hun type:

Kaart postsPerType = posts.stream () .collect (groupingBy (BlogPost :: getType)); 

2.3. groupingBy met een complex Kaart Sleutel type

De classificatiefunctie is niet beperkt tot het retourneren van alleen een scalaire waarde of tekenreekswaarde. De sleutel van de resulterende kaart kan elk object zijn, zolang we ervoor zorgen dat we het noodzakelijke implementeren is gelijk aan en hashcode methoden.

Om de blogposts in de lijst te groeperen op basis van de type en schrijver gecombineerd in een Tuple voorbeeld:

Kaart postsPerTypeAndAuthor = posts.stream () .collect (groupingBy (post -> nieuwe Tuple (post.getType (), post.getAuthor ()))); 

2.4. Wijzigen van de Returned Kaart Waarde type

De tweede overbelasting van groupingBy neemt een extra tweede collector (stroomafwaartse collector) die wordt toegepast op de resultaten van de eerste collector.

Wanneer we een classificatiefunctie specificeren, maar geen stroomafwaartse collector, dan is de toList () verzamelaar wordt achter de schermen gebruikt.

Laten we de toSet () collector als de stroomafwaartse collector en verkrijg een Set van blogposts (in plaats van een Lijst):

Kaart postsPerType = posts.stream () .collect (groupingBy (BlogPost :: getType, toSet ())); 

2.5. Groeperen op meerdere velden

Een andere toepassing van de stroomafwaartse collector is om een ​​secundaire te doen groupingBy naar de resultaten van de eerste groep door.

Om het Lijst van Blogposts eerst door schrijver en dan door type:

Kaart map = posts.stream () .collect (groupingBy (BlogPost :: getAuthor, groupingBy (BlogPost :: getType)));

2.6. Het gemiddelde halen uit gegroepeerde resultaten

Door de stroomafwaartse collector te gebruiken, kunnen we aggregatiefuncties toepassen in de resultaten van de classificatiefunctie.

Om bijvoorbeeld het gemiddelde aantal houdt van voor elke blogpost type:

Kaart averageLikesPerType = posts.stream () .collect (groupingBy (BlogPost :: getType, averagingInt (BlogPost :: getLikes))); 

2.7. De som ophalen uit gegroepeerde resultaten

Om de totale som te berekenen van houdt van voor elk type:

Kaart likesPerType = posts.stream () .collect (groupingBy (BlogPost :: getType, summingInt (BlogPost :: getLikes))); 

2.8. Het maximum of minimum halen uit gegroepeerde resultaten

Een andere aggregatie die we kunnen uitvoeren, is om de blogpost met het maximale aantal likes te krijgen:

Kaart maxLikesPerPostType = posts.stream () .collect (groupingBy (BlogPost :: getType, maxBy (comparingInt (BlogPost :: getLikes))))); 

Evenzo kunnen we de minBy stroomafwaartse verzamelaar om de blogpost met het minimumaantal houdt van.

Merk op dat de maxBy en minBy verzamelaars houden rekening met de mogelijkheid dat de collectie waarop ze worden toegepast leeg zou kunnen zijn. Dit is de reden waarom het waardetype in de kaart is Optioneel.

2.9. Een samenvatting krijgen voor een kenmerk van gegroepeerde resultaten

De Verzamelaars API biedt een samenvattende verzamelaar die we kunnen gebruiken in gevallen waarin we het aantal, de som, het minimum, het maximum en het gemiddelde van een numeriek kenmerk tegelijkertijd moeten berekenen.

Laten we een samenvatting berekenen voor het likes-attribuut van de blogposts voor elk verschillend type:

Kaart likeStatisticsPerType = posts.stream () .collect (groupingBy (BlogPost :: getType, summarizingInt (BlogPost :: getLikes))); 

De IntSummaryStatistics object voor elk type bevat de count, som, gemiddelde, min en max waarden voor de houdt van attribuut. Er zijn aanvullende samenvattingsobjecten voor dubbele en lange waarden.

2.10. Gegroepeerde resultaten toewijzen aan een ander type

We kunnen complexere aggregaties bereiken door een in kaart brengen stroomafwaartse collector naar de resultaten van de classificatiefunctie.

Laten we een aaneenschakeling krijgen van de titels van de berichten voor elk blogbericht type:

Map postsPerType = posts.stream () .collect (groupingBy (BlogPost :: getType, mapping (BlogPost :: getTitle, join (",", "Posttitels: [", "]")))); 

Wat we hier hebben gedaan, is elk in kaart brengen Blogpost instantie naar zijn titel en reduceer vervolgens de stroom van posttitels tot een aaneengeschakeld Draad. In dit voorbeeld is het type Kaart waarde verschilt ook van de standaardwaarde Lijst type.

2.11. Wijziging van de Return Kaart Type

Bij gebruik van de groupingBy verzamelaar, kunnen we geen aannames doen over het type geretourneerde Kaart. Als we specifiek willen zijn over welk type Kaart we willen uit de groep komen, dan kunnen we de derde variant van de groupingBy methode waarmee we het type van de Kaart door een Kaart leveranciersfunctie.

Laten we een EnumMap door een EnumMap leveranciersfunctie aan de groupingBy methode:

EnumMap postsPerType = posts.stream () .collect (groupingBy (BlogPost :: getType, () -> nieuwe EnumMap (BlogPostType.class), toList ())); 

3. Gelijktijdig groupingBy Verzamelaar

Gelijkwaardig aan groupingBy is de groupingByConcurrent collector, die gebruikmaakt van multi-core architecturen. Deze verzamelaar heeft drie overbelaste methoden die precies dezelfde argumenten gebruiken als de respectievelijke overbelaste methoden van de groupingBy verzamelaar. Het retourtype van het groupingByConcurrent collector moet echter een exemplaar zijn van de ConcurrentHashMap klasse of een subklasse ervan.

Om gelijktijdig een groeperingsbewerking uit te voeren, moet de stream parallel zijn:

ConcurrentMap postsPerType = posts.parallelStream () .collect (groupingByConcurrent (BlogPost :: getType)); 

Als we ervoor kiezen om een Kaart leveranciersfunctie aan de groupingByConcurrent collector, dan moeten we ervoor zorgen dat de functie ofwel een ConcurrentHashMap of een subklasse ervan.

4. Java 9-toevoegingen

Java 9 heeft twee nieuwe verzamelaars geïntroduceerd die goed werken met groupingBy; meer informatie over hen is hier te vinden.

5. Conclusie

In dit artikel hebben we het gebruik van de groupingBy verzamelaar aangeboden door de Java 8 Verzamelaars API.

We hebben geleerd hoe groupingBy kan worden gebruikt om een ​​stroom elementen te classificeren op basis van een van hun attributen, en hoe de resultaten van deze classificatie verder kunnen worden verzameld, gemuteerd en teruggebracht tot definitieve containers.

De volledige implementatie van de voorbeelden in dit artikel is te vinden in het GitHub-project.