Analogije API-ja Java 8 Stream u Kotlinu

1. Uvod

Java 8 predstavila je koncept Potoci hijerarhiji kolekcije. Oni omogućuju vrlo moćnu obradu podataka na vrlo čitljiv način, koristeći neke koncepte funkcionalnog programiranja kako bi proces funkcionirao.

Istražit ćemo kako možemo postići istu funkcionalnost pomoću Kotlinovih idioma. Također ćemo pogledati značajke koje nisu dostupne u običnoj Javi.

2. Java protiv Kotlina

U Javi 8, novi se fensi API može koristiti samo u interakciji s java.util.stream.Stream instance.

Dobra stvar je što sve standardne kolekcije - sve što se implementira java.util.Zbirka - imaju određenu metodu stream () koji mogu proizvesti a Stream primjer.

Važno je zapamtiti da Stream nije a Kolekcija.Ne provodi java.util.Zbirka i ne provodi niti jednu uobičajenu semantiku Zbirke na Javi. Sličnije je jednokratnom Iterator u tome što je izvedeno iz a Kolekcija i koristi se za rad kroz njega, izvodeći operacije na svakom elementu koji se vidi.

U Kotlinu sve vrste sakupljanja već podržavaju ove operacije bez potrebe da ih prvo pretvorite. Pretvorba je potrebna samo ako je semantika zbirke pogrešna - npr. A Postavi ima jedinstvene elemente, ali nije uređen.

Jedna je prednost ovoga što nema potrebe za početnom konverzijom iz a Kolekcija u a Stream, i nema potrebe za konačnom pretvorbom iz a Stream natrag u kolekciju - pomoću prikupiti() poziva.

Na primjer, u Javi 8 morali bismo napisati sljedeće:

someList .stream () .map () // neke operacije .collect (Collectors.toList ());

Ekvivalent u Kotlinu vrlo je jednostavan:

someList .map () // neke operacije

Uz to, Java 8 Potoci također se ne mogu ponovno koristiti. Nakon Stream se konzumira, ne može se ponovno koristiti.

Na primjer, sljedeće neće raditi:

Stream someIntegers = integers.stream (); someIntegers.forEach (...); someIntegers.forEach (...); // iznimka

U Kotlinu činjenica da su sve to samo normalne zbirke znači da se taj problem nikada ne pojavljuje. Intermedijarno stanje može se dodijeliti varijablama i brzo dijeliti, i samo radi onako kako bismo očekivali.

3. Lijene sekvence

Jedna od ključnih stvari o Javi 8 Potoci jest da se ocjenjuju lijeno. To znači da se neće izvoditi više posla nego što je potrebno.

To je posebno korisno ako radimo potencijalno skupe operacije na elementima u Stream, ili omogućuje rad s beskonačnim sekvencama.

Na primjer, IntStream.generate proizvest će potencijalno beskonačno Stream cijelih brojeva. Ako nazovemo findFirst () na njemu ćemo dobiti prvi element, a ne naletjeti na beskonačnu petlju.

U Kotlinu su kolekcije željne, a ne lijene. Ovdje je iznimka Slijed, što ipak procjenjuje lijeno.

Ovo je važno razlikovati, kao što pokazuje sljedeći primjer:

val rezultat = listOf (1, 2, 3, 4, 5) .map {n -> n * n} .filter {n -> n <10} .first ()

Verzija Kotlina izvest će pet karta() operacije, pet filtar() operacije i zatim izvući prvu vrijednost. Inačica Java 8 izvest će samo jednu karta() i jedan filtar() jer iz perspektive posljednje operacije više nije potrebno.

Sve zbirke u Kotlinu mogu se pretvoriti u lijeni slijed pomoću asSequence () metoda.

Korištenje a Slijed umjesto a Popis u gornjem primjeru izvodi isti broj operacija kao u Javi 8.

4. Java 8 Stream Operacije

U Javi 8, Stream operacije su podijeljene u dvije kategorije:

  • srednje i
  • terminal

Posredničke operacije u osnovi pretvaraju jednu Stream u drugu lijeno - na primjer, a Stream svih cijelih brojeva u a Stream svih parnih cijelih brojeva.

Opcije terminala posljednji su korak Stream lanca metoda i pokrenuti stvarnu obradu.

U Kotlinu nema te razlike. Umjesto toga, sve su to samo funkcije koje kolekciju uzimaju kao ulaz i proizvode novi izlaz.

Imajte na umu da ako koristimo nestrpljivu kolekciju u Kotlinu, tada se ove operacije odmah procjenjuju, što može biti iznenađujuće u usporedbi s Javom. Ako nam treba da bismo bili lijeni, ne zaboravite se pretvoriti u Slijed prvi.

4.1. Srednje operacije

Gotovo sve posredničke operacije iz API-ja Java 8 Streams imaju ekvivalente u Kotlinu. To ipak nisu posredne operacije - osim u slučaju Slijed klasa - jer rezultiraju potpuno popunjenim zbirkama od obrade ulazne zbirke.

Od ovih operacija postoji nekoliko koji rade potpuno isto - filtar(), karta(), flatMap (), različit () i sortirano () - a neki koji rade isto samo s različitim imenima - ograničiti() je sad uzeti, i preskočiti() je sad pad(). Na primjer:

val oddSquared = listOf (1, 2, 3, 4, 5) .filter {n -> n% 2 == 1} // 1, 3, 5 .map {n -> n * n} // 1, 9 , 25 .kap (1) // 9, 25 .hvatanje (1) // 9

To će vratiti jedinstvenu vrijednost "9" - 3².

Neke od ovih operacija imaju i dodatnu verziju - sufiksiranu riječju "Do" - koja izlazi u pruženu zbirku umjesto da stvara novu.

To može biti korisno za obradu nekoliko ulaznih kolekcija u istu izlaznu kolekciju, na primjer:

val target = mutableList () listOf (1, 2, 3, 4, 5) .filterTo (target) {n -> n% 2 == 0}

Ovo će umetnuti vrijednosti "2" i "4" na popis "cilj".

Jedina operacija koja obično nema izravnu zamjenu je zaviriti () - koristi se u Javi 8 za prevlačenje unosa u Stream usred cjevovoda za preradu bez prekida protoka.

Ako koristimo lijeno Slijed umjesto željne kolekcije, tu je na svaki() funkcija koja izravno zamjenjuje zaviriti funkcija. To ipak postoji samo na ovom jednom razredu, pa moramo biti svjesni koji tip koristimo da bi radio.

Postoje i neke dodatne varijacije standardnih srednjih operacija koje olakšavaju život. Na primjer, filtar operacija ima dodatne verzije filterNotNull (), filterIsInstance (), filterNot () i filterIndexed ().

Na primjer:

listOf (1, 2, 3, 4, 5) .map {n -> n * (n + 1) / 2} .mapIndexed {(i, n) -> "Trokutasti broj $ i: $ n"}

To će proizvesti prvih pet trokutastih brojeva, u obliku "Trokutasti broj 3: 6"

Druga važna razlika je u načinu na koji flatMap operacija radi. U Javi 8 ova je operacija potrebna za vraćanje a Stream primjerice, dok u Kotlinu može vratiti bilo koju vrstu zbirke. To olakšava rad s.

Na primjer:

val words = listOf ("This", "Is", "An", "Example") .flatMap {w -> w.toCharArray ()} // Proizvodi .filter popisa {c -> Character.isUpperCase (c) }

U Javi 8 trebao bi biti umotan drugi redak Arrays.toStream () da bi ovo uspjelo.

4.2. Terminalske operacije

Sve standardne operacije terminala iz API-ja Java 8 Streams imaju izravnu zamjenu u Kotlinu, s jedinom iznimkom prikupiti.

Nekoliko ih ima različita imena:

  • anyMatch () ->bilo koji ()
  • allMatch () ->svi()
  • noneMatch () ->nijedan ()

Neki od njih imaju dodatne varijacije za rad s načinom na koji Kotlin ima razlike - postoje prvi() i firstOrNull (), gdje prvi baca ako je zbirka prazna, ali u suprotnom vraća tip koji se ne može poništiti.

Zanimljiv je slučaj prikupiti. Java 8 to koristi da bi mogla prikupiti sve Stream elementi neke zbirke koristeći predviđenu strategiju.

To omogućuje proizvoljno Kolektor koji će biti isporučen sa svim elementima u zbirci i rezultirat će nekom vrstom rezultata. Oni se koriste iz Kolekcionari pomoćni tečaj, ali po potrebi možemo napisati i vlastiti.

U Kotlinu postoje izravne zamjene za gotovo sve standardne kolektore dostupne izravno kao članovi na samom predmetu kolekcije - nema potrebe za dodatnim korakom s predviđenim kolektorom.

Jedina iznimka ovdje je sažimanjeDvostruko/sažimanjeInt/sažimanjeDugo metode - koje u jednom potezu proizvode srednju vrijednost, brojanje, min, maksimum i zbroj. Svaka od njih može se proizvesti pojedinačno - iako to očito ima veću cijenu.

Alternativno, možemo njime upravljati pomoću petlje za svaku i ručno rukovati ako je potrebno - malo je vjerojatno da će nam trebati svih 5 ovih vrijednosti istodobno, pa trebamo primijeniti samo one važne.

5. Dodatne operacije u Kotlinu

Kotlin dodaje neke dodatne operacije u zbirke koje nisu moguće u Javi 8 bez da ih sami implementiramo.

Neki od njih su jednostavno proširenja standardnih operacija, kao što je gore opisano. Na primjer, moguće je sve radnje izvršiti tako da se rezultat doda postojećoj zbirci, a ne vraća novu zbirku.

Također je u mnogim slučajevima moguće da se lambda ne isporučuje samo s predmetnim elementom već i s indeksom elementa - za zbirke koje se naručuju, pa indeksi imaju smisla.

Postoje i neke operacije koje izričito iskorištavaju nulu Kotlinove sigurnosti - na primjer; možemo izvesti a filterNotNull () na a Popis vratiti a Popis, gdje se uklanjaju sve nule.

Stvarne dodatne operacije koje se mogu izvoditi u Kotlinu, ali ne i u Java 8 Streams, uključuju:

  • ZIP () i otvoriti rajsfešlus() - koriste se za kombiniranje dviju zbirki u jedan slijed parova i obratno za pretvaranje kolekcije parova u dvije zbirke
  • suradnik - koristi se za pretvaranje kolekcije u kartu pružanjem lambda za pretvaranje svakog unosa u zbirci u par ključ / vrijednost na rezultirajućoj mapi

Na primjer:

val brojevi = listOf (1, 2, 3) val riječi = listOf ("jedan", "dva", "tri") brojevi.zip (riječi)

Ovo daje a Popis, s vrijednostima 1 na "jedan", 2 na "dva" i 3 do "tri".

val kvadrata = listOf (1, 2, 3, 4,5). pridruži {n -> n do n * n}

Ovo daje a Karta, gdje su tipke brojevi od 1 do 5, a vrijednosti su kvadratići tih vrijednosti.

6. Sažetak

Većina stream operacija na koje smo navikli iz Jave 8 izravno se mogu koristiti u Kotlinu na standardnim klasama kolekcije, bez potrebe za pretvaranjem u Stream prvi.

Uz to, Kotlin dodaje veću fleksibilnost kako ovo funkcionira, dodavanjem više operacija koje se mogu koristiti i više varijacija postojećih operacija.

Međutim, Kotlin je prema zadanim postavkama nestrpljiv, a ne lijen. To može dovesti do izvođenja dodatnih poslova ako ne budemo pažljivi u vezi s vrstama zbirki koje se koriste.


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