Guide till Java 8 forEach

1. Översikt

Introducerad i Java 8 ger forEach- slingan programmerare ett nytt, koncist och intressant sätt att itera över en samling .

I den här artikeln kommer vi att se hur man använder forEach med samlingar, vilken typ av argument som krävs och hur denna loop skiljer sig från den förbättrade for-loop .

Om du behöver borsta upp några begrepp i Java 8 har vi en samling artiklar som kan hjälpa dig.

2. Grunderna i forEach

I Java har Collection- gränssnittet Iterable som supergränssnitt - och från och med Java 8 har detta gränssnitt ett nytt API:

void forEach(Consumer action)

Enkelt uttryckt, Javadoc of forEach statistik att den "utför den givna åtgärden för varje element i Iterabel tills alla element har bearbetats eller åtgärden ger ett undantag."

Och så, med forEach , kan vi itera över en samling och utföra en given åtgärd på varje element, som alla andra Iterator.

Till exempel kan en for-loop version av iteration och skriva ut en samling av strängar :

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

Vi kan skriva detta med forEach som:

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

3. Använda metoden forEach

Vi använder forEach för att itera över en samling och utföra en viss åtgärd på varje element. Åtgärden som ska utföras ingår i en klass som implementerar konsumentgränssnittet och skickas till forEach som ett argument.

Den Konsument gränssnittet är ett funktionsgränssnitt (ett gränssnitt med en enda abstrakt metod). Den accepterar en inmatning och returnerar inget resultat.

Här är definitionen:

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

Därför, varje implementering, till exempel en konsument som helt enkelt skriver ut en sträng :

Consumer printConsumer = new Consumer() { public void accept(String name) { System.out.println(name); }; };

kan skickas till forEach som ett argument:

names.forEach(printConsumer);

Men det är inte det enda sättet att skapa en åtgärd via en konsument och använda forEach API.

Låt oss se de 3 mest populära sätten vi använder metoden forEach :

3.1. Anonymous Consumer Genomförande

Vi kan starta en implementering av konsumentgränssnittet med hjälp av en anonym klass och sedan använda den som ett argument för forEach- metoden:

Consumer printConsumer= new Consumer() { public void accept(String name) { System.out.println(name); } }; names.forEach(printConsumer);

Detta fungerar bra men om vi analyserar i exemplet ovan ser vi att den faktiska delen som används är koden i accept () -metoden.

Även om Lambda-uttryck nu är normen och det enklare sättet att göra detta är det fortfarande värt att veta hur man implementerar konsumentgränssnittet .

3.2. Ett Lambdauttryck

Den största fördelen med Java 8-funktionella gränssnitt är att vi kan använda Lambda-uttryck för att starta dem och undvika att använda skrymmande anonyma klassimplementeringar.

Eftersom konsumentgränssnittet är ett funktionellt gränssnitt kan vi uttrycka det i Lambda i form av:

(argument) -> { //body }

Därför förenklar vår printConsumer att:

name -> System.out.println(name)

Och vi kan skicka det till forEach som:

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

Sedan introduktionen av Lambda-uttryck i Java 8 är det förmodligen det vanligaste sättet att använda metoden forEach .

Lambdas har en väldigt riktig inlärningskurva, så om du kommer igång går denna skrivning över några bra metoder för att arbeta med den nya språkfunktionen.

3.3. En metodreferens

Vi kan använda metodreferenssyntax istället för den normala Lambda-syntaxen där det redan finns en metod för att utföra en operation på klassen:

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

4. Arbeta med forEach

4.1. Iterera över en samling

Varje iterabel av typen Samling - lista, uppsättning, kö etc. har samma syntax för att använda forEach.

Därför, som vi redan har sett, för att itera delar av en lista:

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

På samma sätt för en uppsättning:

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

Eller låt oss säga för en som också är en samling :

Queue namesQueue = new ArrayDeque(Arrays.asList("Larry", "Steve", "James")); namesQueue.forEach(System.out::println);

4.2. Iterera över en karta - Använd kartor för varje

Kartor är inte spårbara , men de ger sin egen variant av forEach som accepterar en BiConsumer .

En BiConsumer introducerades istället för Consumer i Iterable's forEach så att en åtgärd kan utföras på både nyckeln och värdet på en karta samtidigt.

Låt oss skapa en karta med poster:

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

Låt oss sedan iterera över namesMap med Map's forEach :

namesMap.forEach((key, value) -> System.out.println(key + " " + value));

Som vi kan se här har vi använt en BiConsumer :

(key, value) -> System.out.println(key + " " + value)

för att itera över posterna på kartan .

4.3. Iterera över en karta - av Iterating entrySet

Vi kan också iterera EntrySet för en karta med hjälp av Iterable's forEach.

Eftersom posterna på en karta lagras i en uppsättning som heter EntrySet, kan vi upprepa att med en forEach:

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

5. Foreach vs For-Loop

Ur en enkel synvinkel ger båda slingorna samma funktionalitet - slinga igenom element i en samling.

Huvudskillnaden mellan dem två är att de är olika iteratorer - den förbättrade for-loop är en extern iterator medan den nya forEach- metoden är en intern .

5.1. Intern Iterator - för varje

Denna typ av iterator hanterar iterationen i bakgrunden och låter programmeraren bara koda vad som är tänkt att göras med elementen i samlingen.

Iteratorn hanterar istället iterationen och ser till att bearbeta elementen en efter en.

Låt oss se ett exempel på en intern iterator:

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

I metoden forEach ovan kan vi se att argumentet som tillhandahålls är ett lambdauttryck. Detta innebär att metoden bara behöver veta vad som ska göras och allt iteringsarbete kommer att tas om hand internt.

5.2. Extern Iterator - för-loop

Externa iteratorer blandar vad och hur slingan ska göras.

Uppräkningar , Iteratorer och förbättrad for-loop är alla externa iteratorer (kom ihåg metoderna iterator (), nästa () eller hasNext () ?). I alla dessa iteratorer är det vårt jobb att ange hur man ska utföra iterationer.

Tänk på den här välkända slingan:

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

Även om vi inte uttryckligen åberopar hasNext () eller next () -metoder medan vi itererar över listan, använder den underliggande koden som gör att denna iteration fungerar dessa metoder. Detta innebär att komplexiteten i dessa operationer är dold för programmeraren men den finns fortfarande.

I motsats till en intern iterator där samlingen gör iterationen själv behöver vi här extern kod som tar ut alla element ur samlingen.

6. Slutsats

I den här artikeln visade vi att forEach- slingan är bekvämare än den normala for-loop .

Vi såg också hur forEach- metoden fungerar och vilken typ av implementering som argument kan få för att utföra en åtgärd på varje element i samlingen.

Slutligen är alla utdrag som används i den här artikeln tillgängliga i vårt Github-arkiv.