Sådan får du adgang til en ikterationstæller i en for hver løkke

1. Oversigt

Mens det itererer over data i Java, vil vi muligvis få adgang til både det aktuelle element og dets position i datakilden.

Dette er meget let at opnå i en klassiker til loop, hvor positionen normalt er fokus for loopens beregninger, men det kræver lidt mere arbejde, når vi bruger konstruktioner som for hver loop eller stream.

I denne korte vejledning ser vi på et par måder det til hver operation kan omfatte en tæller.

2. Implementering af en tæller

Lad os starte med et simpelt eksempel. Vi tager en ordnet liste over film og udsender dem med deres placering.

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

2.1. til Sløjfe

EN til loop bruger en tæller til at referere til det aktuelle element, så det er en nem måde at betjene både dataene og dets indeks på listen:

Listeplaceringer = ny ArrayList (); for (int i = 0; i <films.size (); i ++) {String ranking = (i + 1) + ":" + films.get (i); rankings.add (ranking); }

Som dette Liste er sandsynligvis en ArrayList, det driften er effektiv, og ovenstående kode er en simpel løsning på vores problem.

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

Dog kan ikke alle datakilder i Java gentages på denne måde. Sommetider er en tidskrævende operation, eller vi kan kun behandle det næste element i en datakilde ved hjælp af Strøm eller Iterabel.

2.2. til Hver sløjfe

Vi fortsætter med at bruge vores liste over film, men lad os foregive, at vi kun kan gentage det ved hjælp af Java'er til hver konstruktion:

for (String film: IMDB_TOP_MOVIES) {// brug filmværdi}

Her skal vi bruge en separat variabel til at spore det aktuelle indeks. Vi kan konstruere det uden for sløjfen og øge det indeni:

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

Det skal vi bemærke vi er nødt til at forøge tælleren, når den er brugt inden for løkken.

3. En funktionel til Hver

Skrivning af tællerudvidelsen hver gang, vi har brug for det, kan resultere i kodedobling og risikere utilsigtede fejl vedrørende hvornår tællervariablen skal opdateres. Vi kan derfor generalisere ovenstående ved hjælp af Java's funktionelle grænseflader.

For det første skal vi tænke på adfærden inde i sløjfen som forbruger af både genstanden i samlingen og også indekset. Dette kan modelleres ved hjælp af BiConsumer, der definerer en acceptere funktion, der tager to parametre

@FunctionalInterface offentlig grænseflade BiConsumer {ugyldig accept (T t, U u); }

Da indersiden af ​​vores loop er noget, der bruger to værdier, kunne vi skrive en generel looping-operation. Det kunne tage Iterabel af kildedataene, for hvilke for hver sløjfe kører, og BiConsumer for operationen, der skal udføres på hvert element og dets indeks. Vi kan lave dette generisk med typeparameteren T:

statisk tomrum forEachWithCounter (Iterabel kilde, BiConsumer-forbruger) {int i = 0; for (T-vare: kilde) {forbruger.accept (i, vare); i ++; }}

Vi kan bruge dette med vores eksempel på filmrangeringer ved at give implementeringen til BiConsumer som en lambda:

Listeplaceringer = ny ArrayList (); forEachWithCounter (film, (i, film) -> {String ranking = (i + 1) + ":" + film.get (i); rankings.add (ranking);});

4. Tilføjelse af en tæller til for hver med Strøm

Java Strøm API giver os mulighed for at udtrykke, hvordan vores data passerer gennem filtre og transformationer. Det giver også en for hver fungere. Lad os prøve at konvertere det til en operation, der inkluderer tælleren.

Det Stream for hver funktion tager en Forbruger for at behandle det næste emne. Vi kunne dog skabe det Forbruger at holde styr på tælleren og videregive varen til en BiConsumer:

offentlig statisk forbruger med tæller (BiConsumer forbruger) {AtomicInteger tæller = nyt AtomicInteger (0); returvare -> forbruger.accept (counter.getAndIncrement (), vare); }

Denne funktion returnerer en ny lambda. Den lambda bruger AtomicInteger gør indsigelse mod at holde styr på tælleren under iteration. Det getAndIncrement funktion kaldes hver gang der er et nyt emne.

Lambda oprettet af denne funktion delegerer til BiConsumer sendt ind, så algoritmen kan behandle både elementet og dets indeks.

Lad os se dette i brug af vores eksempel på filmrangering mod a Strøm hedder film:

Listeplaceringer = ny ArrayList (); films.forEach (withCounter ((i, film) -> {String ranking = (i + 1) + ":" + film; rankings.add (ranking);}));

Inde i for hver er et opkald til med tæller funktion til at oprette et objekt, der både sporer optællingen og fungerer som Forbruger at den for hver operation overgår også sine værdier.

5. Konklusion

I denne korte artikel har vi set på tre måder at vedhæfte en tæller til Java til hver operation.

Vi så, hvordan man sporer indekset for den aktuelle vare på hver implementering af dem til en løkke. Vi så på, hvordan man generaliserer dette mønster, og hvordan man tilføjer det til streamingoperationer.

Som altid er eksempelkoden til denne artikel tilgængelig på GitHub.


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