Guide til Java 8-gruppering af Collector

1. Introduktion

I denne vejledning vi får se, hvordan grupperingBy samler arbejder ved hjælp af forskellige eksempler.

For at vi kan forstå det materiale, der er dækket af denne vejledning, har vi brug for en grundlæggende viden om Java 8-funktioner. Vi kan se på introduktionen til Java 8 Streams og guiden til Java 8's Collectors for disse grundlæggende.

2. grupperingBy Samlere

Java 8 Strøm API giver os mulighed for at behandle dataindsamlinger på en deklarativ måde.

De statiske fabriksmetoder Collectors.groupingBy () og Collectors.groupingByConcurrent () give os funktionalitet svarende til 'GROUP BY ' klausul i SQL-sproget. Vi bruger dem til at gruppere objekter efter en ejendom og gemme resultater i en Kort eksempel.

De overbelastede metoder til grupperingBy er:

  • For det første med en klassifikationsfunktion som metodeparameter:

statisk samler<>> groupingBy (Funktionsklassifikator)
  • For det andet med en klassifikationsfunktion og en anden samler som metodeparametre:

statisk samler groupingBy (Funktionsklassifikator, Collector nedstrøms)
  • Endelig, med en klassifikationsfunktion, en leverandørmetode (der giver Kort implementering, der indeholder slutresultatet) og en anden samler som metodeparametre:

statisk  Collector groupingBy (Funktionsklassifikator, leverandør mapFactory, Collector downstream)

2.1. Eksempel på opsætning af kode

For at demonstrere brugen af gruppering af (), lad os definere en Blogindlæg klasse (vi bruger en strøm af Blogindlæg objekter):

class BlogPost {String title; Stringforfatter; BlogPostType type; int kan lide; } 

Dernæst BlogPostType:

enum BlogPostType {NYHEDER, ANMELDELSE, GUIDE} 

Derefter Liste af Blogindlæg genstande:

Listeindlæg = Arrays.asList (...);

Lad os også definere en Tuple klasse, der vil blive brugt til at gruppere indlæg efter kombinationen af ​​deres type og forfatter egenskaber:

klasse Tuple {BlogPostType type; Stringforfatter; } 

2.2. Enkel gruppering efter en enkelt kolonne

Lad os starte med det enkleste grupperingBy metode, som kun tager en klassifikationsfunktion som parameter. En klassifikationsfunktion anvendes på hvert element i strømmen. Vi bruger den værdi, der returneres af funktionen, som en nøgle til det kort, vi får fra grupperingBy samler.

At gruppere blogindlæggene på blogindlægslisten efter deres type:

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

2.3. grupperingBy med et kompleks Kort Nøgletype

Klassifikationsfunktionen er ikke begrænset til kun at returnere en skalar- eller strengværdi. Nøglen til det resulterende kort kan være ethvert objekt, så længe vi sørger for, at vi implementerer det nødvendige lige med og hashcode metoder.

At gruppere blogindlæg på listen efter type og forfatter kombineret i en Tuple eksempel:

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

2.4. Ændring af returneret Kort Værditype

Den anden overbelastning af grupperingBy tager en ekstra anden kollektor (downstream collector), der anvendes på resultaterne af den første collector.

Når vi specificerer en klassifikationsfunktion, men ikke en downstream-samler, bliver toList () samler bruges bag kulisserne.

Lad os bruge at sætte() samler som downstream samler og få en Sæt af blogindlæg (i stedet for en Liste):

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

2.5. Gruppering efter flere felter

En anden anvendelse af downstream-samleren er at lave en sekundær grupperingBy til resultaterne af den første gruppe ved.

At gruppere Liste af Blogindlægs først ved forfatter og derefter af type:

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

2.6. Få gennemsnittet fra grupperede resultater

Ved at bruge downstream-samleren kan vi anvende aggregeringsfunktioner i resultaterne af klassificeringsfunktionen.

For eksempel for at finde det gennemsnitlige antal kan lide for hvert blogindlæg type:

Kort gennemsnitLikesPerType = posts.stream () .collect (groupingBy (BlogPost :: getType, averagingInt (BlogPost :: getLikes))); 

2.7. Få summen af ​​grupperede resultater

For at beregne den samlede sum af kan lide for hver type:

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

2.8. Få maksimum eller minimum fra grupperede resultater

En anden sammenlægning, som vi kan udføre, er at få blogindlægget med det maksimale antal likes:

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

På samme måde kan vi anvende minBy downstream-samler for at få blogindlægget med det mindste antal kan lide.

Bemærk, at maxBy og minBy samlere tager højde for muligheden for, at samlingen, som de anvendes på, kan være tom. Derfor er værditypen på kortet Valgfri.

2.9. Sådan får du en oversigt over en egenskab af grupperede resultater

Det Samlere API tilbyder en opsummerende samler, som vi kan bruge i tilfælde, hvor vi har brug for at beregne optælling, sum, minimum, maksimum og gennemsnit af en numerisk attribut på samme tid.

Lad os beregne et resumé for likesattributten for blogindlæggene for hver anden type:

Kort somStatisticsPerType = posts.stream () .collect (groupingBy (BlogPost :: getType, summarizingInt (BlogPost :: getLikes))); 

Det IntSummaryStatistics objekt for hver type indeholder tællings-, sum-, gennemsnits-, min- og maksimumværdierne for kan lide attribut. Yderligere opsummerende objekter findes for dobbelt- og lange værdier.

2.10. Kortlægning af grupperede resultater til en anden type

Vi kan opnå mere komplekse sammenlægninger ved at anvende en kortlægning downstream-samler til resultaterne af klassifikationsfunktionen.

Lad os få en sammenkædning af titels af indlæggene for hvert blogindlæg type:

Kort postsPerType = posts.stream () .collect (groupingBy (BlogPost :: getType, mapping (BlogPost :: getTitle, joining (",", "Posttitler: [", "]")))); 

Hvad vi har gjort her er at kortlægge hver Blogindlæg eksempel til sin titel og reducer derefter strømmen af ​​posttitler til en sammenkædet Snor. I dette eksempel er typen af Kort værdi er også forskellig fra standard Liste type.

2.11. Ændring af retur Kort Type

Når du bruger grupperingBy samler, kan vi ikke antage antagelser om typen af ​​den returnerede Kort. Hvis vi vil være specifikke for, hvilken type Kort vi ønsker at komme fra gruppen ved, så kan vi bruge den tredje variation af grupperingBy metode, der giver os mulighed for at ændre typen af Kort ved at passere en Kort leverandørfunktion.

Lad os hente en EnumMap ved at passere en EnumMap leverandørfunktion til grupperingBy metode:

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

3. Samtidig grupperingBy Samler

Svarende til grupperingBy er groupingByConcurrent samler, der udnytter multi-core arkitekturer. Denne samler har tre overbelastede metoder, der tager nøjagtigt de samme argumenter som de respektive overbelastede metoder til grupperingBy samler. Returtypen af groupingByConcurrent samler skal dog være en forekomst af ConcurrentHashMap klasse eller en underklasse af den.

For at udføre en grupperingsoperation samtidigt skal strømmen være parallel:

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

Hvis vi vælger at videregive en Kort leverandørfunktion til groupingByConcurrent samler, så skal vi sørge for, at funktionen enten returnerer a ConcurrentHashMap eller en underklasse af den.

4. Java 9-tilføjelser

Java 9 introducerede to nye samlere, der fungerer godt sammen med grupperingBy; mere information om dem kan findes her.

5. Konklusion

I denne artikel undersøgte vi brugen af grupperingBy samler tilbydes af Java 8 Samlere API.

Vi lærte hvordan grupperingBy kan bruges til at klassificere en strøm af elementer baseret på en af ​​deres attributter, og hvordan resultaterne af denne klassificering yderligere kan samles, muteres og reduceres til endelige beholdere.

Den komplette implementering af eksemplerne i denne artikel kan findes i GitHub-projektet.