Programiranje

Ponavljanje zbirk v Javi

Kadar koli imate zbirko stvari, boste potrebovali mehanizem za sistematično posredovanje elementov te zbirke. Kot vsakdanji primer si oglejmo televizijski daljinski upravljalnik, ki nam omogoča ponovitev različnih televizijskih kanalov. Podobno v programskem svetu potrebujemo mehanizem za sistematično ponavljanje skozi zbirko predmetov programske opreme. Java vključuje različne mehanizme za ponovitev, vključno z indeks (za iteracijo nad matriko), kazalec (za ponovitev rezultatov poizvedbe v zbirki podatkov), naštevanje (v zgodnjih različicah Jave) in iterator (v novejših različicah Jave).

Iterator vzorec

An iterator je mehanizem, ki omogoča zaporedni dostop do vseh elementov zbirke, pri čemer se za vsak element izvede nekaj operacij. V bistvu iterator omogoča način "zanke" po zaprti zbirki predmetov. Primeri uporabe iteratorjev vključujejo

  • Obiščite vsako datoteko v imeniku (aka mapa) in prikažite njeno ime.
  • Obiščite vsako vozlišče v grafu in ugotovite, ali je dosegljivo iz določenega vozlišča.
  • Obiščite vsako stranko v čakalni vrsti (na primer simulirajte črto v banki) in ugotovite, kako dolgo čaka.
  • Obiščite vsako vozlišče v drevesu abstraktne sintakse prevajalnika (ki ga ustvari razčlenjevalnik) in izvedite semantično preverjanje ali generiranje kode. (V tem kontekstu lahko uporabite tudi vzorec Visitor.)

Za uporabo iteratorjev veljajo nekatera načela: na splošno bi morali imeti možnost, da poteka več prehodov hkrati; to pomeni, da mora iterator omogočiti koncept ugnezdena zanka. Ponavljalec ne bi smel biti uničujoč v smislu, da dejanje ponovitve samo po sebi ne bi smelo spremeniti zbirke. Seveda lahko operacija, ki se izvaja nad elementi v zbirki, nekatere elemente spremeni. Mogoče bi bilo tudi, da bi iterator podpiral odstranjevanje elementa iz zbirke ali vstavljanje novega elementa na določeni točki zbirke, vendar bi morale biti take spremembe izrecne znotraj programa in ne stranski produkt ponovitve. V nekaterih primerih boste morali imeti tudi iteratorje z različnimi metodami prečkanja; na primer prehod drevesa pred ali po naročilu ali prehod grafa po globini in širini.

Ponavljanje zapletenih podatkovnih struktur

Prvič sem se naučil programirati v zgodnji različici FORTRAN-a, kjer je bila edina zmožnost strukturiranja podatkov polje. Hitro sem se naučil, kako itirirati po matriki z uporabo indeksa in DO-zanke. Od tam je bil le kratek miselni preskok do ideje o uporabi skupnega indeksa v več nizov za simulacijo niza zapisov. Večina programskih jezikov ima funkcije, ki so podobne nizom, in podpirajo neposreden zamik po nizih. Toda sodobni programski jeziki podpirajo tudi bolj zapletene podatkovne strukture, kot so seznami, nizi, zemljevidi in drevesa, kjer so zmogljivosti na voljo z javnimi metodami, notranje podrobnosti pa so skrite v zasebnih delih razreda. Programerji morajo biti sposobni prečkati elemente teh podatkovnih struktur, ne da bi izpostavljali njihovo notranjo strukturo, kar je namen iteratorjev.

Iteratorji in tolpa štirih vzorcev

Po navedbah Gang of Four (glej spodaj) je Iterator oblikovalski vzorec je vedenjski vzorec, katerega glavna ideja je "prevzeti odgovornost za dostop in prehod s seznama [izd. think collection] in ga postavi v objekt iteratorja. "Ta članek ne govori toliko o vzorcu iteratorja kot o tem, kako se iteratorji uporabljajo v praksi. Za popolno pokrivanje vzorca bi bilo treba razpravljati o tem, kako bi bil iterator zasnovan, udeleženci ( predmetov in razredov) pri načrtovanju, možnih nadomestnih izvedbah in kompromisih različnih oblikovalskih alternativ. Raje se osredotočim na to, kako se iteratorji uporabljajo v praksi, vendar vas bom opozoril na nekaj virov za preiskovanje vzorca in vzorcev oblikovalcev na splošno:

  • Vzorci oblikovanja: elementi predmetno usmerjene programske opreme za večkratno uporabo (Addison-Wesley Professional, 1994), ki so ga napisali Erich Gamma, Richard Helm, Ralph Johnson in John Vlissides (znan tudi kot Gang of Four ali preprosto GoF), je dokončni vir za učenje o vzorcih oblikovanja. Čeprav je bila knjiga prvič objavljena leta 1994, ostaja klasika, kar dokazuje tudi podatek, da je bilo objavljenih več kot 40 tiskov.
  • Bob Tarr, predavatelj na Univerzi Maryland v okrožju Baltimore, ima odličen niz diapozitivov za svoj tečaj oblikovalskih vzorcev, vključno s svojim uvodom v vzorec Iterator.
  • Serija JavaWorld Davida Gearyja Vzorci Java oblikovanja predstavlja številne vzorce Gang of Four, vključno z vzorci Singleton, Observer in Composite. Tudi na JavaWorldu najnovejši tridelni pregled vzorčnih vzorcev Jeffa Friesena vključuje vodnik po vzorcih GoF.

Aktivni iteratorji v primerjavi s pasivnimi iteratorji

Obstajata dva splošna pristopa k izvajanju iteratorja, odvisno od tega, kdo nadzira iteracijo. Za aktivni iterator (poznan tudi kot eksplicitni iterator ali zunanji iterator), odjemalec nadzoruje iteracijo v smislu, da odjemalec ustvari iterator, mu pove, kdaj naj napreduje do naslednjega elementa, preizkusi, ali je bil vsak element obiskan itd. Ta pristop je pogost v jezikih, kot je C ++, in prav njemu je v knjigi GoF namenjeno največ pozornosti. Čeprav so iteratorji v Javi imeli različne oblike, je bila uporaba aktivnega iteratorja v bistvu edina izvedljiva možnost pred Java 8.

Za pasivni iterator (znan tudi kot implicitni iterator, notranji iterator, ali iterator povratnega klica), iterator sam nadzira ponovitev. Naročnik v bistvu reče iteratorju: "izvedite to operacijo nad elementi v zbirki." Ta pristop je pogost v jezikih, kot je LISP, ki zagotavljajo anonimne funkcije ali zapiranja. Z izdajo Jave 8 je ta pristop k ponovitvi zdaj primerna alternativa za programerje Java.

Sheme poimenovanja Java 8

Čeprav ni tako slaba kot Windows (NT, 2000, XP, VISTA, 7, 8, ...), zgodovina različic Java vključuje več shem poimenovanja. Ali bi morali za začetek standardno različico Jave označiti kot "JDK", "J2SE" ali "Java SE"? Številke različic Jave so se začele precej preprosto - 1.0, 1.1 itd. - vendar se je vse spremenilo z različico 1.5, ki je nosila blagovno znamko Java (ali JDK) 5. Pri sklicevanju na zgodnje različice Jave uporabljam besedne zveze, kot sta "Java 1.0" ali "Java 1.1, "vendar po peti različici Jave uporabljam besedne zveze, kot sta" Java 5 "ali" Java 8. "

Za ponazoritev različnih pristopov k ponovitvi v Javi potrebujem primer zbirke in nekaj, kar je treba narediti z njenimi elementi. Za začetni del tega članka bom uporabil zbirko nizov, ki predstavljajo imena stvari. Za vsako ime v zbirki bom preprosto natisnil njegovo vrednost na standardni izhod. Te osnovne ideje se zlahka razširijo na zbirke bolj zapletenih predmetov (na primer zaposlenih) in kjer je obdelava vsakega predmeta nekoliko bolj vključena (na primer vsakemu visoko ocenjenemu zaposlenemu povišanje 4,5 odstotka).

Druge oblike ponovitve v Javi 8

Osredotočam se na ponavljanje zbirk, v Javi pa obstajajo druge, bolj specializirane oblike ponovitve. Na primer, lahko uporabite JDBC ResultSet iteracijo vrstic, vrnjenih iz poizvedbe SELECT v relacijsko bazo podatkov, ali uporabite Optični bralnik iteracijo nad vhodnim virom.

Ponavljanje z razredom Enumeration

V Java 1.0 in 1.1 sta bila dva osnovna razreda zbiranja Vektor in Hashtable, in vzorec oblikovanja Iterator je bil izveden v razredu z imenom Naštevanje. Za nazaj je bilo to slabo ime za razred. Ne mešajte razreda Naštevanje s konceptom vrste enum, ki se je pojavilo šele na Javi 5. Danes oboje Vektor in Hashtable so generični razredi, vendar takrat generiki niso bili del jezika Java. Koda za obdelavo vektorja nizov z uporabo Naštevanje bi izgledalo nekako kot Seznam 1.

Seznam 1. Uporaba oštevilčenja za iteracijo po vektorju nizov

 Imena vektorjev = novo Vector (); // ... v zbirko dodamo nekaj imen Enumeration e = names.elements (); while (e.hasMoreElements ()) {String name = (String) e.nextElement (); System.out.println (ime); } 

Ponavljanje z razredom Iterator

Java 1.2 je predstavil razrede zbiranja, ki jih vsi poznamo in jih imamo radi, vzorec oblikovanja Iterator pa je bil izveden v razredu z ustreznim imenom Iterator. Ker v Javi 1.2 še nismo imeli generikov, je oddajanje predmeta, vrnjenega iz datoteke Iterator je bilo še vedno potrebno. Pri različicah Java od 1.2 do 1.4 je ponavljanje seznama nizov morda podobno seznamu 2.

Seznam 2. Uporaba iteratorja za iteracijo po seznamu nizov

 Imena seznamov = novo LinkedList (); // ... v zbirko dodamo nekaj imen Iterator i = names.iterator (); while (i.hasNext ()) {String name = (String) i.next (); System.out.println (ime); } 

Ponavljanje z generičnimi zdravili in izboljšano for-zanko

Java 5 nam je dala generike, vmesnik Ponovljivoin izboljšana zanka for. Izboljšana zanka for je ena mojih najljubših majhnih dodatkov k Javi. Ustvarjanje iteratorja in klici na njegov hasNext () in Naslednji() metode v kodi niso izrecno izražene, vendar vseeno potekajo v zakulisju. Čeprav je koda bolj kompaktna, še vedno uporabljamo aktivni iterator. Z uporabo Jave 5 bi bil naš primer videti nekako tako, kot vidite na seznamu 3.

Seznam 3. Uporaba generikov in izboljšane zanke for za iteracijo po seznamu nizov

 Imena seznamov = novo LinkedList (); // ... v zbirko dodamo nekaj imen za (String name: names) System.out.println (name); 

Java 7 nam je dal diamantnega operaterja, ki zmanjšuje podrobnost generikov. Časi, ko je bilo treba po ponovitvi generičnega razreda uporabiti primer za generiranje razreda, so minili novo operater! V Javi 7 bi lahko poenostavili prvo vrstico v zgornjem seznamu 3 na naslednje:

 Imena seznamov = novo LinkedList (); 

Blag odmev proti generikom

Zasnova programskega jezika vključuje kompromise med koristmi jezikovnih lastnosti v primerjavi s kompleksnostjo, ki jo nalagajo sintaksi in semantiki jezika. Za generike nisem prepričan, da so koristi večje od zapletenosti. Generiki so rešili težavo, ki je pri Javi nisem imel. Na splošno se strinjam z mnenjem Kena Arnolda, ko izjavi: "Generiki so napaka. To ni težava, ki temelji na tehničnih nesoglasjih. To je temeljni problem oblikovanja jezika [...] Zapletenost Jave je bila turbo polnjena, kar se mi zdi sorazmerno majhna korist. "

Na srečo je sicer načrtovanje in izvajanje generičnih razredov včasih preveč zapleteno, vendar sem ugotovil, da je uporaba generičnih razredov v praksi običajno preprosta.

Ponavljanje z metodo forEach ()

Preden se poglobimo v funkcije ponovitve Java 8, razmislimo, kaj je narobe s kodo, prikazano v prejšnjih seznamih - kar v resnici ni nič. V trenutno postavljenih aplikacijah je na milijone vrstic kode Java, ki uporabljajo aktivne iteratorje, podobne tistim, ki so prikazani v mojih seznamih. Java 8 preprosto ponuja dodatne zmogljivosti in nove načine izvajanja ponovitve. Za nekatere scenarije so lahko novi načini boljši.

Glavne novosti v Java 8 so osredotočene na lambda izraze, skupaj s povezanimi funkcijami, kot so tokovi, sklici na metode in funkcionalni vmesniki. Te nove funkcije v Javi 8 nam omogočajo, da resno razmislimo o uporabi pasivnih iteratorjev namesto bolj običajnih aktivnih iteratorjev. Zlasti Ponovljivo vmesnik ponuja pasivni iterator v obliki privzete metode, imenovane za vsakogar().

A privzeta metoda, nova novost v Javi 8, je metoda v vmesniku s privzeto izvedbo. V tem primeru je za vsakogar() metoda se dejansko izvaja z aktivnim iteratorjem na način, podoben tistemu, ki ste ga videli v seznamu 3.

Razredi zbiranja, ki se izvajajo Ponovljivo (na primer vsi razredi seznamov in nizov) imajo zdaj za vsakogar() metoda. Ta metoda ima en parameter, ki je funkcionalen vmesnik. Zato je dejanski parameter prenesen na za vsakogar() metoda je kandidat za lambda izraz. Z uporabo funkcij Jave 8 bi se naš tekalni primer razvil v obrazec, prikazan v seznamu 4.

Seznam 4. Iteracija v Javi 8 z metodo forEach ()

 Imena seznamov = novo LinkedList (); // ... v zbirko imen dodamo nekaj imen.forEach (name -> System.out.println (name)); 

Upoštevajte razliko med pasivnim iteratorjem v seznamu 4 in aktivnim iteratorjem v prejšnjih treh uvrstitvah. V prvih treh uvrstitvah struktura zanke nadzoruje iteracijo in med vsakim prehodom skozi zanko je predmet s seznama pridobljen in nato natisnjen. Na seznamu 4 ni eksplicitne zanke. Preprosto povemo za vsakogar() način, kaj storiti s predmeti na seznamu - v tem primeru predmet preprosto natisnemo. Nadzor nad ponovitvijo je znotraj za vsakogar() metoda.

Ponavljanje s tokom Java

Zdaj pa razmislimo o tem, da naredimo nekaj bolj zapletenega kot preprosto tiskanje imen na našem seznamu. Denimo, da želimo na primer prešteti število imen, ki se začnejo s črko A. Bolj zapleteno logiko bi lahko implementirali kot del lambda izraza ali pa uporabili novi Stream API Java 8. Upoštevajmo slednji pristop.

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