Analogy Java 8 Stream API v Kotline
1. Úvod
Java 8 predstavila koncept Prúdy do hierarchie zbierok. Umožňujú veľmi výkonné spracovanie údajov veľmi čitateľným spôsobom s využitím niektorých koncepcií funkčného programovania, aby proces fungoval.
Budeme skúmať, ako môžeme dosiahnuť rovnakú funkcionalitu pomocou Kotlinových idiómov. Ďalej sa pozrieme na funkcie, ktoré nie sú k dispozícii v obyčajnej Jave.
2. Java vs. Kotlin
V prostredí Java 8 je možné nové fantastické rozhranie API použiť iba pri interakcii s programom java.util.stream.Stream inštancie.
Dobrá vec je, že všetky štandardné kolekcie - všetko, čo sa dá implementovať java.util.Collection - mať konkrétnu metódu Prúd() ktoré môžu produkovať a Prúd inštancia.
Je dôležité mať na pamäti, že Prúd nie je Zbierka.Nerealizuje java.util.Collection a neimplementuje žiadnu z bežných sémantík jazyka Zbierky v Jave. Je to skôr podobné jednorazu Iterátor v tom, že je odvodené od a Zbierka a používa sa na jeho prepracovanie a vykonávanie operácií s každým viditeľným prvkom.
V Kotline tieto operácie už podporujú všetky typy zbierok bez toho, aby ste ich museli najskôr konvertovať. Konverzia je potrebná iba v prípade, že je sémantika zbierky nesprávna - napr Nastaviť má jedinečné prvky, ale nie je usporiadané.
Výhodou je, že nie je potrebná počiatočná konverzia z a Zbierka do a Prúd, a nie je potrebná konečná konverzia z a Prúd späť do zbierky - pomocou zbierať () hovory.
Napríklad v prostredí Java 8 by sme museli napísať nasledovné:
someList .stream () .map () // niektoré operácie .collect (Collectors.toList ());
Ekvivalent v Kotline je veľmi jednoduchý:
someList .map () // niektoré operácie
Ďalej Java 8 Prúdy sú tiež opakovane nepoužiteľné. Po Prúd je spotrebovaný, nedá sa znovu použiť.
Napríklad nebude fungovať nasledovné:
Streamovať someIntegers = integers.stream (); someIntegers.forEach (...); someIntegers.forEach (...); // výnimka
Skutočnosť, že v Kotline ide iba o bežné zbierky, znamená, že tento problém nikdy nenastane. Medziľahlý stav je možné priradiť k premenným a rýchlo zdieľať, a funguje len tak, ako by sme očakávali.
3. Lenivé sekvencie
Jedna z kľúčových vecí Java 8 Prúdy je to, že sa hodnotia lenivo. To znamená, že sa nevykoná viac práce, ako je potrebné.
To je obzvlášť užitočné, ak robíme potenciálne nákladné operácie s prvkami v Prúd, alebo umožňuje pracovať s nekonečnými sekvenciami.
Napríklad, IntStream.generate vyprodukuje potenciálne nekonečno Prúd celých čísel. Ak zavoláme findFirst () na ňom dostaneme prvý prvok a nenarazíme na nekonečnú slučku.
V Kotline sú zbierky skôr nedočkavé ako lenivé. Výnimka tu je Postupnosť, ktorá hodnotí lenivo.
Je potrebné si uvedomiť toto dôležité rozlíšenie, ako ukazuje nasledujúci príklad:
výsledok výsledku = listOf (1, 2, 3, 4, 5) .map {n -> n * n} .filter {n -> n <10} .first ()
Kotlinova verzia toho predvedie päť mapa () operácie, päť filter () operácie a potom extrahujte prvú hodnotu. Verzia Java 8 bude vykonávať iba jednu mapa () a jeden filter () pretože z pohľadu poslednej operácie už nie sú potrebné ďalšie.
Všetky zbierky v Kotline je možné prevádzať na lenivú sekvenciu pomocou asSequence () metóda.
Pomocou a Postupnosť namiesto a Zoznam vo vyššie uvedenom príklade vykonáva rovnaký počet operácií ako v prostredí Java 8.
4. Java 8 Prúd Operácie
V prostredí Java 8 Prúd operácie sú rozdelené do dvoch kategórií:
- stredne pokročilý a
- terminál
Medzistupne v zásade prevádzajú jednu Prúd do iného lenivo - napríklad a Prúd všetkých celých čísel do a Prúd všetkých párnych celých čísel.
Možnosti terminálu sú posledným krokom v Prúd reťazec metód a spustiť samotné spracovanie.
V Kotline nie je také rozlíšenie. Namiesto toho to všetko sú iba funkcie, ktoré berú kolekciu ako vstup a vytvárajú nový výstup.
Všimnite si, že ak v Kotline používame dychtivú zbierku, potom sa tieto operácie vyhodnotia okamžite, čo môže byť v porovnaní s Javou prekvapujúce. Ak potrebujeme, aby to bolo lenivé, nezabudnite previesť na a Postupnosť najprv.
4.1. Sprostredkujúce operácie
Takmer všetky prechodné operácie z rozhrania Java 8 Streams API majú v Kotline ekvivalenty. Nejde však o medzioperačné operácie - s výnimkou prípadu Postupnosť trieda - pretože výsledkom sú plne obsadené kolekcie zo spracovania vstupnej kolekcie.
Z týchto operácií existuje niekoľko, ktoré fungujú úplne rovnako - filter (), mapa (), flatMap (), odlišný() a zoradené () - a niektoré, ktoré fungujú rovnako, len s rôznymi názvami - limit () je teraz vziaťa preskočiť () je teraz pokles(). Napríklad:
val oddSquared = listOf (1, 2, 3, 4, 5). filter {n -> n% 2 == 1} // 1, 3, 5 .map {n -> n * n} // 1, 9 , 25 .drop (1) // 9, 25. Take (1) // 9
Takto sa vráti jediná hodnota „9“ - 3².
Niektoré z týchto operácií majú aj ďalšiu verziu - doplnenú o slovo „To“ - ktoré sa vydávajú do poskytnutej zbierky namiesto produkcie novej.
To môže byť užitočné pri spracovaní viacerých vstupných kolekcií do tej istej výstupnej kolekcie, napríklad:
val target = mutableList () listOf (1, 2, 3, 4, 5) .filterTo (target) {n -> n% 2 == 0}
Týmto vložíte hodnoty „2“ a „4“ do zoznamu „cieľ“.
Jedinou operáciou, ktorá zvyčajne nemá priamu náhradu, je nahliadnuť () - používaný v Java 8 na iteráciu nad položkami v Prúd uprostred spracovateľského potrubia bez prerušenia toku.
Ak používame lenivého Postupnosť namiesto nedočkavej zbierky potom existuje na každom() funkcia, ktorá priamo nahrádza nakuknúť funkcie. Toto však existuje iba v tejto jednej triede, a preto musíme vedieť, aký typ používame, aby fungoval.
Existujú aj niektoré ďalšie variácie štandardných medzioperácií, ktoré uľahčujú život. Napríklad filter prevádzka má ďalšie verzie filterNotNull (), filterIsInstance (), filterNot () a filterIndexed ().
Napríklad:
listOf (1, 2, 3, 4, 5) .map {n -> n * (n + 1) / 2} .mapIndexed {(i, n) -> "Trojuholníkové číslo $ i: $ n"}
Toto vyprodukuje prvých päť trojuholníkových čísel v tvare „Trojuholníkové číslo 3: 6“.
Ďalším dôležitým rozdielom je spôsob, akým flatMap prevádzka funguje. V prostredí Java 8 sa táto operácia vyžaduje na vrátenie a Prúd napríklad v Kotline môže vrátiť akýkoľvek typ zbierky. To uľahčuje prácu.
Napríklad:
val letters = listOf ("This", "Is", "An", "Example") .flatMap {w -> w.toCharArray ()} // Vytvorí zoznam .filter {c -> Character.isUpperCase (c) }
V prostredí Java 8 by bolo treba zabaliť druhý riadok Arrays.toStream () aby to fungovalo.
4.2. Prevádzka terminálu
Všetky štandardné operácie terminálu z rozhrania Java 8 Streams API majú priame nahradenie v Kotline, s jedinou výnimkou zbierať.
Pár z nich má rôzne mená:
- anyMatch () ->akýkoľvek()
- allMatch () ->všetko ()
- noneMatch () ->žiadny ()
Niektoré z nich majú ďalšie variácie, aby pracovali s tým, ako má Kotlin rozdiely - existujú najprv() a firstOrNull (), kde najprv hodí, ak je kolekcia prázdna, ale inak vráti typ s nulovou hodnotou.
Zaujímavý prípad je zbierať. Java 8 to využíva na to, aby mohla zhromaždiť všetky Prúd prvky do nejakého zberu pomocou poskytnutej stratégie.
To umožňuje svojvoľnosť Zberateľ ktoré budú poskytnuté so všetkými prvkami v zbierke a budú produkovať výstup nejakého druhu. Tieto sa používajú z Zberatelia pomocná trieda, ale v prípade potreby si môžeme napísať aj vlastnú.
V Kotline existujú priame náhrady za takmer všetky štandardné kolektory dostupné priamo ako členovia v samotnom objekte kolekcie - nie je potrebný ďalší krok so zabezpečeným zberačom.
Jedinou výnimkou je tu zhrnutieDouble/summarizingInt/sumarizujúciDlhé metódy - ktoré produkujú priemer, počet, min., max. a súčet naraz. Každý z nich sa dá vyrobiť individuálne - aj keď to má samozrejme vyššiu cenu.
Prípadne to môžeme spravovať pomocou slučky for-each a v prípade potreby ju zvládnuť ručne - je nepravdepodobné, že budeme potrebovať všetkých 5 týchto hodnôt súčasne, takže musíme implementovať iba tie, ktoré sú dôležité.
5. Dodatočné operácie v Kotline
Kotlin pridáva do zbierok ďalšie operácie, ktoré nie sú možné v prostredí Java 8 bez toho, aby sme ich sami implementovali.
Niektoré z nich sú iba rozšírením štandardných operácií, ako je opísané vyššie. Napríklad je možné vykonať všetky operácie tak, že sa výsledok pridá do existujúcej kolekcie namiesto vrátenia novej kolekcie.
V mnohých prípadoch je tiež možné nechať lambdu vybaviť nielen príslušným prvkom, ale aj indexom prvku - pre kolekcie, ktoré sú usporiadané, a preto majú indexy zmysel.
Existujú aj niektoré operácie, ktoré výslovne využívajú nulovú bezpečnosť Kotlina - napríklad; môžeme vykonať a filterNotNull () na a Zoznam vrátiť sa a Zoznam, kde sú odstránené všetky nulové hodnoty.
Skutočné ďalšie operácie, ktoré je možné vykonať v Kotline, ale nie v tokoch Java 8, zahŕňajú:
- PSČ() a rozbaliť () - slúžia na spojenie dvoch zbierok do jednej postupnosti párov a naopak na konverziu zbierky párov do dvoch zbierok
- spolupracovník - slúži na prevod zbierky na mapu poskytnutím lambda na prevod každej položky v zbierke na pár kľúč / hodnota vo výslednej mape
Napríklad:
val numbers = listOf (1, 2, 3) val words = listOf ("one", "two", "three") numbers.zip (words)
Týmto sa získa a Zoznam
val squares = listOf (1, 2, 3, 4,5). asociujte {n -> n až n * n}
Týmto sa získa a Mapa, kde klávesy sú číslice 1 až 5 a hodnoty sú štvorce týchto hodnôt.
6. Zhrnutie
Väčšina streamovacích operácií, na ktoré sme zvyknutí z Javy 8, je priamo použiteľná v Kotline na štandardných triedach Collection, bez nutnosti prevádzania na Prúd najprv.
Okrem toho Kotlin pridáva viac flexibility do toho, ako to funguje, pridaním ďalších operácií, ktoré je možné použiť, a väčšej variácie existujúcich operácií.
Kotlin je však štandardne nedočkavý, nie lenivý. To môže spôsobiť vykonanie ďalších prác, ak si nebudeme dávať pozor na typy kolekcií, ktoré sa používajú.