Vejledning til Java 8 forEach

1. Oversigt

Introduceret i Java 8, for hver loop giver programmører en ny, kortfattet og interessant måde at gentage en samling på.

I denne artikel vil vi se, hvordan du bruger for hver med samlinger, hvilken slags argument det tager, og hvordan denne sløjfe adskiller sig fra den forbedrede for-loop.

Hvis du har brug for at børste nogle koncepter i Java 8, har vi en samling artikler, der kan hjælpe dig.

2. Grundlæggende om for hver

I Java er Kollektion interface har Iterabel som sin supergrænseflade - og startende med Java 8 har denne grænseflade en ny API:

ugyldigt for hver (forbrugerhandling)

Kort sagt, Javadoc af for hver statistik at det "Udfører den givne handling for hvert element i Iterable, indtil alle elementer er blevet behandlet, eller handlingen kaster en undtagelse."

Og så med for hver, kan vi gentage over en samling og udføre en given handling på hvert element som ethvert andet Iterator.

For eksempel en for-loop version af iterering og udskrivning a Kollektion af Strenge:

for (String name: names) {System.out.println (name); }

Vi kan skrive dette ved hjælp af for hver som:

names.forEach (navn -> {System.out.println (navn);});

3. Brug af for hver Metode

Vi bruger for hver at gentage en samling og udføre en bestemt handling på hvert element. Handlingen, der skal udføres, er indeholdt i en klasse, der implementerer Forbruger interface og overføres til for hver som et argument.

Det Forbruger interface er en funktionel interface (en interface med en enkelt abstrakt metode). Den accepterer et input og returnerer intet resultat.

Her er definitionen:

@FunctionalInterface offentlig grænseflade Forbruger {ugyldig accept (T t); }

Derfor er enhver implementering, for eksempel en forbruger, der blot udskriver en Snor:

Forbruger printConsumer = ny forbruger () {offentlig ugyldig accept (strengnavn) {System.out.println (navn); }; };

kan overføres til for hver som argument:

names.forEach (printConsumer);

Men det er ikke den eneste måde at skabe en handling via en forbruger og bruge for hver API.

Lad os se de 3 mest populære måder, hvorpå vi vil bruge for hver metode:

3.1. Anonym Forbruger Implementering

Vi kan igangsætte en implementering af Forbruger interface ved hjælp af en anonym klasse og derefter anvende den som et argument på for hver metode:

Forbruger printConsumer = ny forbruger () {offentlig ugyldig accept (strengnavn) {System.out.println (navn); }}; names.forEach (printConsumer);

Dette fungerer godt, men hvis vi analyserer ved eksemplet ovenfor, ser vi, at den aktuelle del, der er til brug, er koden inde i acceptere() metode.

Selvom Lambda-udtryk nu er normen og den lettere måde at gøre dette på, er det stadig værd at vide, hvordan man implementerer Forbruger interface.

3.2. Et Lambda-udtryk

Den største fordel ved Java 8-funktionelle grænseflader er, at vi kan bruge Lambda-udtryk til at instantiere dem og undgå at bruge store anonyme klasseimplementeringer.

Som Forbruger Interface er en funktionel interface, vi kan udtrykke det i Lambda i form af:

(argument) -> {// body}

Derfor er vores printConsumer forenkler til:

navn -> System.out.println (navn)

Og vi kan videregive det til for hver som:

names.forEach (navn -> System.out.println (navn));

Siden introduktionen af ​​Lambda-udtryk i Java 8 er dette sandsynligvis den mest almindelige måde at bruge for hver metode.

Lambdas har en meget reel indlæringskurve, så hvis du kommer i gang, går denne opskrivning på nogle gode fremgangsmåder til at arbejde med den nye sprogfunktion.

3.3. En metodehenvisning

Vi kan bruge metodereferencesyntaks i stedet for den normale Lambda-syntaks, hvor der allerede findes en metode til at udføre en operation på klassen:

names.forEach (System.out :: println);

4. Arbejde med for hver

4.1. Iterering over en samling

Enhver iterabel type Samling - liste, sæt, kø osv. har den samme syntaks til brug for hver.

Derfor, som vi allerede har set, at gentage elementer på en liste:

Liste navne = Arrays.asList ("Larry", "Steve", "James"); names.forEach (System.out :: println);

Tilsvarende for et sæt:

Indstil uniqueNames = new HashSet (Arrays.asList ("Larry", "Steve", "James")); uniqueNames.forEach (System.out :: println);

Eller lad os sige for en hvilket også er en Kollektion:

KønavnKø = ny ArrayDeque (Arrays.asList ("Larry", "Steve", "James")); namesQueue.forEach (System.out :: println);

4.2. Iterering over et kort - Brug af kort for hver

Kort er det ikke Iterabel, men det gør de give deres egen variant af for hver der accepterer en BiConsumer.

EN BiConsumer blev introduceret i stedet for Forbruger i Iterable's for hver så en handling kan udføres på både nøglen og værdien af ​​en Kort samtidigt.

Lad os oprette en Kort har poster:

Map namesMap = new HashMap (); namesMap.put (1, "Larry"); namesMap.put (2, "Steve"); namesMap.put (3, "James");

Lad os derefter gentage det namesMap ved hjælp af kort for hver:

namesMap.forEach ((nøgle, værdi) -> System.out.println (nøgle + "" + værdi));

Som vi kan se her, har vi brugt en BiConsumer:

(nøgle, værdi) -> System.out.println (nøgle + "" + værdi)

for at gentage posterne i Kort.

4.3. Itererer over en Kort - af Iterating entrySet

Vi kan også gentage Indgangssæt af en Kort ved hjælp af Iterable's for hver.

Siden indtastningerne af en Kort opbevares i en Sæt hedder Indgangssæt, vi kan gentage det ved hjælp af en for hver:

namesMap.entrySet (). forEach (post -> System.out.println (entry.getKey () + "" + entry.getValue ()));

5. Foreach vs For-Loop

Fra et simpelt synspunkt giver begge sløjfer den samme funktionalitet - gennemløb elementer i en samling.

Hovedforskellen mellem de to er, at de er forskellige iteratorer - den forbedrede for-loop er en ekstern iterator, mens den nye for hver metoden er en intern.

5.1. Intern Iterator - for hver

Denne type iterator styrer iterationen i baggrunden og lader programmøren bare kode, hvad der skal gøres med elementerne i samlingen.

Iteratoren styrer i stedet iteration og sørger for at behandle elementerne en efter en.

Lad os se et eksempel på en intern iterator:

names.forEach (navn -> System.out.println (navn));

I for hver Metoden ovenfor kan vi se, at argumentet er et lambda-udtryk. Dette betyder, at metoden kun behøver at vide hvad der skal gøres og alt iteringsarbejdet vil blive taget hånd om internt.

5.2. Ekstern Iterator - for-loop

Eksterne iteratorer blander hvad og hvordan løkken skal gøres.

Tællinger, Iteratorer og forbedret for-loop er alle eksterne iteratorer (husk metoderne iterator (),Næste() eller hasNext () ? ). I alle disse iteratorer er det vores job at specificere, hvordan man udfører iterationer.

Overvej denne velkendte sløjfe:

for (String name: names) {System.out.println (name); }

Skønt vi ikke udtrykkeligt påberåber os hasNext () eller Næste() metoder, mens det itererer over listen, bruger den underliggende kode, der får denne iteration til at fungere, disse metoder. Dette indebærer, at kompleksiteten af ​​disse operationer er skjult for programmøren, men den eksisterer stadig.

I modsætning til en intern iterator, hvor samlingen udfører selve iterationen, kræver vi her ekstern kode, der tager hvert element ud af samlingen.

6. Konklusion

I denne artikel viste vi, at for hver loop er mere praktisk end det normale for-loop.

Vi så også, hvordan for hver metoden fungerer, og hvilken form for implementering der kan modtages som et argument for at udføre en handling på hvert element i samlingen.

Endelig er alle de uddrag, der bruges i denne artikel, tilgængelige i vores Github-arkiv.