Sobes.tech
Middle

Ako pracovať s tokmi (streams) v Kotlin?

sobes.tech AI

Odpoveď od AI

V Kotlinu sa spracovaním tokov dát (analóg Java 8 Streams API) sa používajú rozšírené funkcie pre kolekcie, ktoré poskytuje štandardná knižnica funkcií. Nie je to samostatné API, ale súbor metód z kotlin.collections.

Hlavné operácie s tokmi sú rozdelené na:

  • Medziprodukčné (Intermediate) operácie: Vrátia novú kolekciu a sú "lenivé" (vykonávajú sa iba pri volaní terminálnej operácie). Príklady: filter, map, flatMap, distinct, sorted, take, drop.
  • Terminálne (Terminal) operácie: Ukončia sekvenciu operácií a vrátia výsledok (nie kolekciu). Spúšťajú vykonanie všetkých medziprodukčných operácií. Príklady: forEach, reduce, fold, count, sum, average, min, max, toList, toSet, toMap.

Existuje aj koncept sekvencií (sequences), ktorý zabezpečuje explicitnú lenivosť. Sekvencie spracovávajú prvky jeden po druhom podľa potreby, čo môže byť efektívnejšie pre veľké kolekcie alebo dlhé reťazce operácií:

  • Vytvorenie sekvencie: collection.asSequence()
  • Prevod späť na kolekciu: sequence.toList()
// Príklad použitia štandardných funkcií na spracovanie tokov
fun processCollection() {
    val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    val evenSquares = numbers
        .filter { it % 2 == 0 } // Medziprodukčná operácia: filtrujeme párne čísla
        .map { it * it }     // Medziprodukčná operácia: umocňujeme na druh

    println(evenSquares) // Terminálna operácia: vypisuje kolekciu (vykonáva filtráciu a map)

    val sumOfOdd = numbers
        .filter { it % 2 != 0 } // Medziprodukčná
        .sum()                // Terminálna (vykonávaná): počíta súčet nepárnych

    println(sumOfOdd)
}

// Príklad použitia sekvencií
fun processSequence() {
    val largeCollection = (1..1_000_000).toList()

    // S kolekciami (ne lenivými): filter a map sa vykonajú pre všetky prvky
    val resultCollection = largeCollection
        .filter { it % 2 == 0 }
        .map { it * 2 }
        .take(10) // take(10) sa vykoná po filtrovaní a map pre všetky

    println("Result Collection size: ${resultCollection.size}")

    // So sekvenciami (lenivými): filter, map a take sa vykonávajú po jednom prvku
    val resultSequence = largeCollection.asSequence()
        .filter { it % 2 == 0 } // filter sa vykonáva pre každý prvok
        .map { it * 2 }     // map sa vykonáva pre každý prešlý filter prvok
        .take(10)         // take(10) zastaví spracovanie po 10 prvkoch

    println("Result Sequence size: ${resultSequence.toList().size}") // toList() spúšťa vykonanie sekvencie
}

fun main() {
    processCollection()
    processSequence()
}

Porovnávacia tabuľka štandardných funkcií a sequences z hľadiska lenivosti:

Vlastnosť Štandardné funkcie kolekcií Sequences
Lenivosť Nezrejmá lenivosť pre medziprodukčné operácie, ale terminálna operácia spracováva celý reťazec naraz Jasná lenivosť: operácie sa vykonávajú po prvku podľa potreby
Efektívnosť Môže byť menej efektívna pre veľké kolekcie a dlhé reťazce operácií kvôli medziprodukčným kolekciám Efektívnejšia pre veľké kolekcie a dlhé reťazce operácií
Použitie Vhodná pre malé a stredné kolekcie Vhodná pre veľké kolekcie alebo operácie, ktoré môžu byť skrátené skôr (take, find, first, any, all, none)

Použitie štandardných funkcií na kolekciách je väčšinou dostatočné a stručnejšie. Sequences sa odporúčajú, keď je potrebná jasná lenivosť na optimalizáciu výkonu pri práci s veľkými objemami dát.