Veiledning til Java 8 forEach

1. Oversikt

Introdusert i Java 8, for hver loop gir programmerere med en ny, kortfattet og interessant måte å gjenta over en samling.

I denne artikkelen vil vi se hvordan du bruker for hver med samlinger, hva slags argument det tar og hvordan denne sløyfen skiller seg fra den forbedrede for-loop.

Hvis du trenger å pusse opp noen konsepter i Java 8, har vi en samling artikler som kan hjelpe deg.

2. Grunnleggende om for hver

I Java er Samling grensesnitt har Iterabel som supergrensesnitt - og fra og med Java 8 har dette grensesnittet en ny API:

ugyldig forEach (forbrukerhandling)

Enkelt sagt, Javadoc av for hver statistikk som det "Utfører den gitte handlingen for hvert element i Iterabelen til alle elementene er behandlet eller handlingen gir et unntak."

Og så, med for hver, kan vi gjentas over en samling og utføre en gitt handling på hvert element, som alle andre Iterator.

For eksempel, a for-loop versjon av iterering og utskrift a Samling av Strenger:

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

Vi kan skrive dette ved hjelp av for hver som:

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

3. Bruke for hver Metode

Vi bruker for hver å gjentas over en samling og utføre en viss handling på hvert element. Handlingen som skal utføres inngår i en klasse som implementerer Forbruker grensesnitt og overføres til for hver som argument.

De Forbruker grensesnitt er et funksjonelt grensesnitt (et grensesnitt med en enkelt abstrakt metode). Den godtar en innspill og returnerer ikke noe resultat.

Her er definisjonen:

@FunctionalInterface public interface Consumer {void accept (T t); }

Derfor, enhver implementering, for eksempel en forbruker som bare skriver ut en String:

Forbruker printConsumer = ny forbruker () {public void accept (String name) {System.out.println (name); }; };

kan overføres til for hver som argument:

names.forEach (printConsumer);

Men det er ikke den eneste måten å lage en handling via en forbruker og bruke for hver API.

La oss se de 3 mest populære måtene vi vil bruke for hver metode:

3.1. Anonym Forbruker Gjennomføring

Vi kan sette i gang en implementering av Forbruker grensesnitt ved hjelp av en anonym klasse og deretter bruke den som et argument på for hver metode:

Forbruker printConsumer = ny forbruker () {public void accept (String name) {System.out.println (name); }}; names.forEach (printConsumer);

Dette fungerer bra, men hvis vi analyserer i eksemplet ovenfor, ser vi at den faktiske delen som er i bruk er koden i aksepterer() metode.

Selv om Lambda-uttrykk nå er normen og den enkleste måten å gjøre dette på, er det fortsatt verdt å vite hvordan du implementerer Forbruker grensesnitt.

3.2. Et Lambda-uttrykk

Den største fordelen med Java 8-funksjonelle grensesnitt er at vi kan bruke Lambda-uttrykk for å stimulere dem og unngå å bruke store anonyme klasseimplementeringer.

Som Forbruker Grensesnitt er et funksjonelt grensesnitt, vi kan uttrykke det i Lambda i form av:

(argument) -> {// body}

Derfor vår printConsumer forenkler å:

navn -> System.out.println (navn)

Og vi kan overføre det til for hver som:

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

Siden introduksjonen av Lambda-uttrykk i Java 8 er dette sannsynligvis den vanligste måten å bruke for hver metode.

Lambdas har en veldig reell læringskurve, så hvis du begynner, går denne oppskriften gjennom noen gode fremgangsmåter for å arbeide med den nye språkfunksjonen.

3.3. En metodehenvisning

Vi kan bruke referansesyntaks for metoden i stedet for den normale Lambda-syntaksen der en metode allerede eksisterer for å utføre en operasjon på klassen:

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

4. Arbeide med for hver

4.1. Iterere over en samling

Enhver iterabel av typen Samling - liste, sett, kø etc. har samme syntaks for bruk for hver.

Derfor, som vi allerede har sett, å gjenta elementer av en liste:

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

Tilsvarende for et sett:

Sett unike navn = nye HashSet (Arrays.asList ("Larry", "Steve", "James")); uniqueNames.forEach (System.out :: println);

Eller la oss si for en som også er en Samling:

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

4.2. Iterere over et kart - ved hjelp av kart for hver

Kart er ikke Iterabel, men det gjør de gi sin egen variant av for hver som aksepterer en BiConsumer.

EN BiConsumer ble introdusert i stedet for Forbruker i Iterable's for hver slik at en handling kan utføres på både nøkkelen og verdien av en Kart samtidig.

La oss lage en Kart har oppføringer:

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

Neste, la oss gjenta namesMap ved hjelp av Map's for hver:

namesMap.forEach ((nøkkel, verdi) -> System.out.println (nøkkel + "" + verdi));

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

(nøkkel, verdi) -> System.out.println (nøkkel + "" + verdi)

å gjenta over oppføringene til Kart.

4.3. Iterere over en Kart - av Iterating entrySet

Vi kan også gjenta EntrySet av en Kart bruker Iterable's for hver.

Siden oppføringene til en Kart lagres i en Sett kalt Oppføringssett, vi kan gjenta at bruk av a for hver:

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

5. Foreach vs For-Loop

Fra et enkelt synspunkt gir begge sløyfer den samme funksjonaliteten - gå gjennom elementer i en samling.

Hovedforskjellen mellom de to er at de er forskjellige 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 typen iterator administrerer iterasjonen i bakgrunnen og lar programmereren bare kode det som er ment å bli gjort med elementene i samlingen.

Iteratoren styrer i stedet iterasjonen og sørger for å behandle elementene en etter en.

La oss se et eksempel på en intern iterator:

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

I for hver Metoden ovenfor kan vi se at argumentet som er gitt er et lambdauttrykk. Dette betyr at metoden bare trenger å vite hva skal gjøres og alt arbeidet med iterering vil bli tatt hånd om internt.

5.2. Ekstern Iterator - for-loop

Eksterne iteratorer blander hva og hvordan løkken skal gjøres.

Oppregninger, Iteratorer og forbedret for-loop er alle eksterne iteratorer (husk metodene iterator (),neste () eller hasNext () ? ). I alle disse iteratorene er det vår jobb å spesifisere hvordan man skal utføre iterasjoner.

Tenk på denne kjente sløyfen:

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

Selv om vi ikke uttrykkelig påberoper oss hasNext () eller neste () metoder mens det gjentas over listen, bruker den underliggende koden som får denne iterasjonen til å fungere disse metodene. Dette innebærer at kompleksiteten i disse operasjonene er skjult for programmereren, men den eksisterer fortsatt.

I motsetning til en intern iterator der samlingen gjør iterasjonen selv, krever vi her ekstern kode som tar hvert element ut av samlingen.

6. Konklusjon

I denne artikkelen viste vi at for hver loop er mer praktisk enn det normale for-loop.

Vi så også hvordan for hver metoden fungerer og hva slags implementering som kan mottas som et argument for å utføre en handling på hvert element i samlingen.

Til slutt er alle utdragene som brukes i denne artikkelen tilgjengelig i Github-arkivet.


$config[zx-auto] not found$config[zx-overlay] not found