Sobes.tech
Middle

Hogyan dolgozzunk a Kotlinban a stream-ekkel?

sobes.tech MI

Válasz az MI-től

Kotlinben adatfolyamokkal (Java 8 Streams API-hoz hasonló) történő munkához a gyűjteményekhez kiterjesztő függvényeket használnak, amelyeket a standard függvénykönyvtár biztosít. Ez nem egy külön API, hanem a kotlin.collections metódusainak készlete.

A fő műveletek az adatfolyamokkal a következők szerint oszlanak:

  • Közbenső (Intermediate) műveletek: Új gyűjteményt adnak vissza, és "lusta" módon hajtódnak végre (csak a végső művelet hívásakor). Példák: filter, map, flatMap, distinct, sorted, take, drop.
  • Végső (Terminal) műveletek: Befejezik a műveletsorozatot, és eredményt adnak vissza (nem gyűjteményt). Aktiválják az összes közbenső művelet végrehajtását. Példák: forEach, reduce, fold, count, sum, average, min, max, toList, toSet, toMap.

Emellett létezik az szekvenciák (sequences) fogalma, amely explicit módon biztosítja a lustaságot. A szekvenciák elemeket egymás után dolgoznak fel, szükség szerint, ami hatékonyabb lehet nagy gyűjtemények vagy hosszú műveletsorozatok esetén:

  • Szekvencia létrehozása: collection.asSequence()
  • Visszaalakítás gyűjteménnyé: sequence.toList()
// Példa a standard függvények adatfolyam-kezelésére
fun processCollection() {
    val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    val evenSquares = numbers
        .filter { it % 2 == 0 } // Közbenső művelet: páros számok szűrése
        .map { it * it }     // Közbenső művelet: négyzetre emelés

    println(evenSquares) // Végső művelet: gyűjtemény kiírása (a szűrés és a map végrehajtása)

    val sumOfOdd = numbers
        .filter { it % 2 != 0 } // Közbenső
        .sum()                // Végső (végrehajtott): páratlanok összege

    println(sumOfOdd)
}

// Szekvencia használat példája
fun processSequence() {
    val largeCollection = (1..1_000_000).toList()

    // Gyűjteményekkel (nem lustán): a filter és map az összes elemre végrehajtódik
    val resultCollection = largeCollection
        .filter { it % 2 == 0 }
        .map { it * 2 }
        .take(10) // take(10) a filter és map után hajtódik végre minden elemre

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

    // Szekvenciákkal (lustán): a filter, map és take elem-enként végrehajtódik
    val resultSequence = largeCollection.asSequence()
        .filter { it % 2 == 0 } // filter minden elemre
        .map { it * 2 }     // map minden átment elemre
        .take(10)         // take(10) megállítja a feldolgozást 10 elem után

    println("Result Sequence size: ${resultSequence.toList().size}") // toList() végrehajtja a szekvencia futtatását
}

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

Az összehasonlító táblázat a standard függvények és a sequences között a lustaság szempontjából:

Jellemző Standard gyűjteményfüggvények Sequences
Lustaság Rejtett lustaság a közbenső műveleteknél, de a végső művelet az egész láncot feldolgozza Egyértelmű lustaság: a műveletek elem-enként végrehajtódnak szükség szerint
Hatékonyság Kevésbé hatékony lehet nagy gyűjtemények és hosszú láncok esetén a közbenső gyűjtemények miatt Hatékonyabb nagy gyűjtemények és hosszú láncok esetén
Alkalmazás Kisebb és közepes gyűjteményekhez alkalmas Nagy gyűjteményekhez vagy olyan műveletekhez, amelyek korán megszakíthatók (take, find, first, any, all, none)

A standard függvények gyűjteményeken való használata általában elegendő és tömörebb. A sequences akkor ajánlottak, ha explicit lustaságra van szükség a nagy adatmennyiség hatékony kezelése érdekében.