Sobes.tech
Middle

Kako raditi sa tokovima (streams) u Kotlinu?

sobes.tech АИ

Одговор од АИ

U Kotlinu za rad sa tokovima podataka (analogno Java 8 Streams API) koriste se funkcije proširenja za kolekcije, koje pruža standardna biblioteka funkcija. To nije poseban API, već skup metoda iz kotlin.collections.

Glavne operacije sa tokovima dele se na:

  • Međuprostorne (Intermediate) operacije: Vraćaju novu kolekciju i "lenje" se (izvršavaju se samo pri pozivu terminalne operacije). Primeri: filter, map, flatMap, distinct, sorted, take, drop.
  • Terminalne (Terminal) operacije: Završavaju niz operacija i vraćaju rezultat (ne kolekciju). Aktiviraju izvršenje svih međuprostornih operacija. Primeri: forEach, reduce, fold, count, sum, average, min, max, toList, toSet, toMap.

Takođe postoji koncept sekvenci (sequences), koji obezbeđuje eksplicitnu lenjivost. Sekvence obrađuju elemente jedan po jedan po potrebi, što može biti efikasnije za velike kolekcije ili duge lance operacija:

  • Kreiranje sekvence: collection.asSequence()
  • Pretvaranje nazad u kolekciju: sequence.toList()
// Primer korišćenja standardnih funkcija za obradu tokova
fun processCollection() {
    val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    val evenSquares = numbers
        .filter { it % 2 == 0 } // Međuprostorna operacija: filtriramo parne brojeve
        .map { it * it }     // Međuprostorna operacija: kvadriramo

    println(evenSquares) // Terminalna operacija: prikazuje kolekciju (izvršava filtriranje i map)

    val sumOfOdd = numbers
        .filter { it % 2 != 0 } // Međuprostorna
        .sum()                // Terminalna (izvršava se): računa zbir neparnih

    println(sumOfOdd)
}

// Primer korišćenja sekvenci
fun processSequence() {
    val largeCollection = (1..1_000_000).toList()

    // Sa kolekcijama (ne lenjive): filter i map će se izvršiti za sve elemente
    val resultCollection = largeCollection
        .filter { it % 2 == 0 }
        .map { it * 2 }
        .take(10) // take(10) se izvršava nakon filtriranja i map za sve

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

    // Sa sekvencama (lenjive): filter, map i take se izvršavaju po elementu
    val resultSequence = largeCollection.asSequence()
        .filter { it % 2 == 0 } // filter se izvršava za svaki element
        .map { it * 2 }     // map se izvršava za svaki prošli filter element
        .take(10)         // take(10) zaustavlja obradu nakon 10 elemenata

    println("Result Sequence size: ${resultSequence.toList().size}") // toList() pokreće izvršenje sekvence
}

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

Tabela poređenja standardnih funkcija i sequences u pogledu lenjivosti:

Osobina Standardne funkcije kolekcija Sequences
Lijenost Nejasna lenjivost za međuprostorne operacije, ali terminalna operacija obrađuje ceo lanac Jasna lenjivost: operacije se izvršavaju po elementu po potrebi
Efikasnost Može biti manje efikasno za velike kolekcije i duge lance zbog međuprostornih kolekcija Efikasnije za velike kolekcije i duge lance operacija
Primena Pogodno za manje i srednje kolekcije Pogodno za velike kolekcije ili operacije koje mogu biti prekinute ranije (take, find, first, any, all, none)

Korišćenje standardnih funkcija na kolekcijama je najčešće dovoljno i kraće. Sequences se preporučuju kada je potrebna jasna lenjivost za optimizaciju performansi pri radu sa velikim količinama podataka.