Ako získať prístup k počítadlu iterácie v každej slučke

1. Prehľad

Pri iterácii údajov v prostredí Java môžeme mať záujem o prístup k aktuálnej položke aj k jej umiestneniu v zdroji údajov.

To je v klasike veľmi ľahké pre slučka, kde je poloha zvyčajne stredobodom výpočtov slučky, ale vyžaduje si trochu viac práce, keď použijeme konštrukty ako pre každú slučku alebo prúd.

V tomto krátkom tutoriáli sa pozrieme na niekoľko spôsobov pre každá operácia môže obsahovať počítadlo.

2. Implementácia počítadla

Začnime jednoduchým príkladom. Vezmeme objednaný zoznam filmov a vydáme ich s ich poradím.

Zoznam IMDB_TOP_MOVIES = Arrays.asList ("Vykúpenie Shawshank", "Krstný otec", "Krstný otec II", "Temný rytier");

2.1. pre Slučka

A pre loop používa počítadlo na odkazovanie na aktuálnu položku, takže je to jednoduchý spôsob, ako pracovať s údajmi a ich indexom v zozname:

Zoznam hodnotení = nový ArrayList (); for (int i = 0; i <movies.size (); i ++) {Poradie reťazcov = (i + 1) + ":" + films.get (i); rankings.add (hodnotenie); }

Ako toto Zoznam je pravdepodobne ArrayList, dostať prevádzka je efektívna a vyššie uvedený kód predstavuje jednoduché riešenie nášho problému.

assertThat (getRankingsWithForLoop (IMDB_TOP_MOVIES)) .containsExactly ("1: Vykúpenie z väznice Shawshank", "2: Krstný otec", "3: Krstný otec II", "4: Temný rytier");

Nie všetky zdroje údajov v Jave však možno týmto spôsobom iterovať. Niekedy dostať je operácia náročná na čas, alebo môžeme ďalší prvok zdroja údajov spracovať iba pomocou Prúd alebo Iterable.

2.2. pre Každá slučka

Naďalej budeme používať náš zoznam filmov, ale predstierajme, že ho môžeme iterovať iba pomocou Javy pre každý konštrukt:

for (String movie: IMDB_TOP_MOVIES) {// use movie value}

Tu musíme na sledovanie aktuálneho indexu použiť samostatnú premennú. Môžeme to skonštruovať mimo slučky a zvýšiť ju vo vnútri:

int i = 0; pre (Reťazcový film: filmy) {Reťazcové hodnotenie = (i + 1) + ":" + film; rankings.add (hodnotenie); i ++; }

Mali by sme si to všimnúť po použití musíme počítadlo zvýšiť v rámci slučky.

3. Funkčné pre Každý

Zápis rozšírenia počítadla zakaždým, keď to potrebujeme, môže mať za následok duplikáciu kódu a môže riskovať náhodné chyby týkajúce sa času aktualizácie premennej počítadla. Vyššie uvedené teda môžeme zovšeobecniť pomocou funkčných rozhraní Java.

Po prvé, mali by sme myslieť na správanie vo vnútri cyklu ako na spotrebiteľa jednak položky v kolekcii, jednak indexu. To je možné modelovať pomocou BiConsumer, ktorý definuje an súhlasiť funkcia, ktorá má dva parametre

@FunctionalInterface verejné rozhranie BiConsumer {void accept (T t, U u); }

Pretože vo vnútri našej slučky sa nachádza niečo, čo používa dve hodnoty, mohli by sme napísať všeobecnú operáciu opakovania. Mohlo by to trvať Iterable zdrojových údajov, cez ktoré bude pre každú slučku prebiehať, a BiConsumer aby sa operácia vykonala s každou položkou a jej indexom. Toto môžeme urobiť všeobecným parametrom type T:

static void forEachWithCounter (Iterable source, BiConsumer consumer) {int i = 0; pre (položka T: zdroj) {consumer.accept (i, item); i ++; }}

Môžeme to použiť pri našom príklade hodnotení filmov tým, že poskytneme implementáciu pre BiConsumer ako lambda:

Zoznam hodnotení = nový ArrayList (); forEachWithCounter (movies, (i, movie) -> {String ranking = (i + 1) + ":" + films.get (i); rankings.add (ranking);});

4. Pridanie počítadla do pre každý s Prúd

Java Prúd API nám umožňuje vyjadriť, ako naše údaje prechádzajú filtrami a transformáciami. Poskytuje tiež a pre každý funkcie. Pokúsme sa to previesť na operáciu, ktorá obsahuje počítadlo.

The Stream pre každého funkcia trvá a Spotrebiteľ na spracovanie ďalšej položky. Mohli by sme to však vytvoriť Spotrebiteľ sledovať počítadlo a odovzdať predmet na a BiConsumer:

public static Consumer withCounter (BiConsumer consumer) {AtomicInteger counter = new AtomicInteger (0); vrátiť položku -> consumer.accept (counter.getAndIncrement (), položka); }

Táto funkcia vráti novú lambdu. Táto lambda používa AtomicInteger objekt na sledovanie počitadla počas iterácie. The getAndIncrement funkcia sa volá zakaždým, keď je nová položka.

Lambda vytvorená touto funkciou deleguje na BiConsumer odovzdané, aby algoritmus mohol spracovať položku aj jej index.

Uvidíme, čo to využije náš príklad zoradenia filmov proti a Prúd zavolal filmy:

Zoznam hodnotení = nový ArrayList (); movies.forEach (withCounter ((i, movie) -> {String ranking = (i + 1) + ":" + movie; rankings.add (ranking);}));

Vnútri pre každý je výzva na withCounter Funkcia vytvorí objekt, ktorý sleduje počet a funguje ako Spotrebiteľ že pre každý prevádzka odovzdáva svoje hodnoty tiež.

5. Záver

V tomto krátkom článku sme sa zaoberali tromi spôsobmi, ako pripojiť počítadlo k Jave pre každú operáciu.

Videli sme, ako sledovať index aktuálnej položky pri každej ich implementácii pre slučka. Potom sme sa pozreli na to, ako tento vzor zovšeobecniť a ako ho pridať do streamovacích operácií.

Ako vždy je ukážkový kód tohto článku k dispozícii na GitHub.