Hvordan få tilgang til en iterasjonsteller i en for hver løkke

1. Oversikt

Mens det gjentas over data i Java, kan det være lurt å få tilgang til både den nåværende varen og posisjonen i datakilden.

Dette er veldig enkelt å oppnå i en klassiker til loop, der posisjonen vanligvis er fokus for løkkenes beregninger, men det krever litt mer arbeid når vi bruker konstruksjoner som for hver løkke eller strøm.

I denne korte opplæringen vil vi se på noen måter det til hver operasjon kan omfatte en teller.

2. Implementere en teller

La oss starte med et enkelt eksempel. Vi tar en bestilt liste over filmer og sender dem ut med rangering.

Liste IMDB_TOP_MOVIES = Arrays.asList ("The Shawshank Redemption", "The Godfather", "The Godfather II", "The Dark Knight");

2.1. til Løkke

EN til loop bruker en teller for å referere til det nåværende elementet, så det er en enkel måte å operere over både dataene og indeksen i listen:

Liste rangering = ny ArrayList (); for (int i = 0; i <films.size (); i ++) {Stringrangering = (i + 1) + ":" + films.get (i); rankings.add (rangering); }

Som dette Liste er sannsynligvis en ArrayList, den driften er effektiv, og ovennevnte kode er en enkel løsning på problemet vårt.

assertThat (getRankingsWithForLoop (IMDB_TOP_MOVIES)) .containsExactly ("1: The Shawshank Redemption", "2: The Godfather", "3: The Godfather II", "4: The Dark Knight");

Imidlertid kan ikke alle datakilder i Java gjentas på denne måten. Noen ganger er en tidskrevende operasjon, eller vi kan bare behandle det neste elementet i en datakilde ved hjelp av Strøm eller Iterabel.

2.2. til Hver løkke

Vi fortsetter å bruke listen vår over filmer, men la oss late som om vi bare kan gjenta det ved hjelp av Java for hver konstruksjon:

for (strengfilm: IMDB_TOP_MOVIES) {// bruk filmverdi}

Her må vi bruke en egen variabel for å spore den nåværende indeksen. Vi kan konstruere det utenfor løkken, og øke det inne:

int i = 0; for (String movie: films) {String ranking = (i + 1) + ":" + movie; rankings.add (rangering); i ++; }

Vi bør merke oss det vi må øke telleren etter at den har blitt brukt innenfor løkken.

3. En funksjonell til Hver

Hvis du skriver tellerutvidelsen hver gang vi trenger det, kan det føre til duplisering av kode og kan risikere utilsiktet feil om når tellervariabelen skal oppdateres. Vi kan derfor generalisere det ovennevnte ved hjelp av Java's funksjonelle grensesnitt.

Først bør vi tenke på atferden i løkken som forbruker av både varen i samlingen og også indeksen. Dette kan modelleres ved hjelp av BiConsumer, som definerer en aksepterer funksjon som tar to parametere

@FunctionalInterface offentlig grensesnitt BiConsumer {ugyldig godta (T t, U u); }

Siden innsiden av sløyfen vår er noe som bruker to verdier, kan vi skrive en generell sløyfeoperasjon. Det kan ta Iterabel av kildedataene, for hvilken for hver sløyfe vil kjøres, og BiConsumer for at operasjonen skal utføres på hvert element og dets indeks. Vi kan lage dette generisk med typeparameteren T:

statisk tomrom forEachWithCounter (Iterabel kilde, BiConsumer forbruker) {int i = 0; for (T item: source) {consumer.accept (i, item); i ++; }}

Vi kan bruke dette med vårt eksempel på filmrangeringer ved å gi implementeringen av BiConsumer som en lambda:

Liste rangering = ny ArrayList (); forEachWithCounter (filmer, (i, film) -> {Stringrangering = (i + 1) + ":" + film.get (i); rankings.add (rangering);});

4. Legge til en teller til for hver med Strøm

Java Strøm API lar oss uttrykke hvordan dataene våre går gjennom filtre og transformasjoner. Det gir også en for hver funksjon. La oss prøve å konvertere det til en operasjon som inkluderer telleren.

De Strøm for hver funksjonen tar en Forbruker for å behandle neste element. Vi kunne imidlertid skape det Forbruker for å holde oversikt over disken og sende varen til en BiConsumer:

public static Consumer withCounter (BiConsumer consumer) {AtomicInteger counter = new AtomicInteger (0); returvare -> forbruker.accept (counter.getAndIncrement (), vare); }

Denne funksjonen returnerer en ny lambda. At lambda bruker AtomicInteger innvending for å holde oversikt over disken under iterasjonen. De getAndIncrement funksjonen kalles hver gang det er et nytt element.

Lambda opprettet av denne funksjonen delegerer til BiConsumer sendt inn slik at algoritmen kan behandle både varen og indeksen.

La oss se dette i bruk av vårt eksempel på filmrangering mot a Strøm kalt filmer:

Liste rangering = ny ArrayList (); movies.forEach (withCounter ((i, movie) -> {String ranking = (i + 1) + ":" + movie; rankings.add (ranking);}));

Inne i for hver er et kall til withCounter funksjon for å lage et objekt som både sporer tellingen og fungerer som Forbruker at for hver operasjonen overfører også verdiene.

5. Konklusjon

I denne korte artikkelen har vi sett på tre måter å feste en teller til Java på til hver operasjon.

Vi så hvordan vi kan spore indeksen til gjeldende vare på hver implementering av dem til en løkke. Vi så på hvordan man generaliserer dette mønsteret og hvordan man legger det til streamingoperasjoner.

Som alltid er eksempelkoden for denne artikkelen tilgjengelig på GitHub.


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