Salta el contingut

Col·leccions i operadors funcionals

Les col·leccions son estructures de dades fonamentals en Kotlin que permeten agrupar i manipular conjunts de valors. Kotlin ofereix una API molt rica d'operadors funcionals que es poden aplicar tant a llistes i mapes com a Flows.

Documentacio oficial: Collections overview - Kotlin

1. Tipus de col·leccions

Kotlin distingeix entre col·leccions immutables (nomes lectura) i mutables (lectura i escriptura):

// Immutables (recomanades per defecte)
val noms: List<String> = listOf("Anna", "Marc", "Laia")
val edats: Set<Int> = setOf(20, 22, 20) // sense duplicats: {20, 22}
val mapa: Map<String, Int> = mapOf("Anna" to 20, "Marc" to 22)

// Mutables
val nomsMutables: MutableList<String> = mutableListOf("Anna", "Marc")
nomsMutables.add("Laia")
Tipus Immutable Mutable
Llista ordenada List / listOf() MutableList / mutableListOf()
Conjunt sense duplicats Set / setOf() MutableSet / mutableSetOf()
Parells clau-valor Map / mapOf() MutableMap / mutableMapOf()

Immutable per defecte

Es recomanable utilitzar col·leccions immutables sempre que sigui possible. Aixo evita modificacions accidentals i fa el codi mes segur.

2. Operador map

Transforma cada element de la col·leccio aplicant una funcio:

val noms = listOf("anna", "marc", "laia")

val nomsMajuscules = noms.map { it.uppercase() }
// Resultat: ["ANNA", "MARC", "LAIA"]

Exemple amb objectes:

data class Alumne(val nom: String, val nota: Double)

val alumnes = listOf(
    Alumne("Anna", 8.5),
    Alumne("Marc", 6.0),
    Alumne("Laia", 9.2)
)

val nomsAlumnes: List<String> = alumnes.map { it.nom }
// Resultat: ["Anna", "Marc", "Laia"]

map en Flows

L'operador map tambe funciona amb Flows, transformant cada valor emes:

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

fun temperaturesEnFahrenheit(celsius: Flow<Double>): Flow<Double> {
    return celsius.map { it * 9 / 5 + 32 }
}

3. Operador filter

Retorna nomes els elements que compleixen una condicio:

val numeros = listOf(1, 2, 3, 4, 5, 6, 7, 8)

val parells = numeros.filter { it % 2 == 0 }
// Resultat: [2, 4, 6, 8]

Exemple amb objectes:

val aprovats = alumnes.filter { it.nota >= 5.0 }
val excel·lents = alumnes.filter { it.nota >= 9.0 }

filter en Flows

import kotlinx.coroutines.flow.filter

fun temperaturesAltes(temperatures: Flow<Double>): Flow<Double> {
    return temperatures.filter { it > 30.0 }
}

4. Encadenar operadors

Un dels punts forts de Kotlin es que els operadors es poden encadenar per fer transformacions complexes de forma llegible:

val alumnes = listOf(
    Alumne("Anna", 8.5),
    Alumne("Marc", 4.0),
    Alumne("Laia", 9.2),
    Alumne("Pol", 6.5)
)

val nomsAprovats = alumnes
    .filter { it.nota >= 5.0 }
    .map { it.nom }
    .sorted()
// Resultat: ["Anna", "Laia", "Pol"]

Amb Flows funciona igual:

fun nomsAlumnesAprovats(alumnes: Flow<Alumne>): Flow<String> {
    return alumnes
        .filter { it.nota >= 5.0 }
        .map { it.nom }
}

5. Altres operadors utils

forEach

Executa una accio per cada element (no retorna una nova col·leccio):

noms.forEach { nom ->
    println("Hola, $nom!")
}

sortedBy

Ordena els elements segons un criteri:

val ordenatsPerNota = alumnes.sortedBy { it.nota }
val ordenatsDesc = alumnes.sortedByDescending { it.nota }

firstOrNull / find

Retorna el primer element que compleix la condicio, o null si no n'hi ha cap:

val primerExcellent = alumnes.firstOrNull { it.nota >= 9.0 }
// Equivalent:
val primerExcellent2 = alumnes.find { it.nota >= 9.0 }

any / all / none

Comproven condicions sobre la col·leccio:

val hiHaSuspesos = alumnes.any { it.nota < 5.0 }    // true
val totsAprovats = alumnes.all { it.nota >= 5.0 }    // false
val capSuspes = alumnes.none { it.nota < 5.0 }       // false

groupBy

Agrupa elements per un criteri:

val perNota = alumnes.groupBy {
    if (it.nota >= 5.0) "Aprovat" else "Suspes"
}
// Resultat: {"Aprovat": [Anna, Laia, Pol], "Suspes": [Marc]}

sumOf

Suma un valor numeric de cada element:

val sumaTotal = alumnes.sumOf { it.nota }

6. Operador combine (exclusiu de Flows)

L'operador combine combina els valors mes recents de dos o mes Flows. Cada cop que un dels Flows emet un nou valor, es recalcula el resultat:

import kotlinx.coroutines.flow.combine

val nom: Flow<String> = // ...
val edat: Flow<Int> = // ...

val perfil: Flow<String> = nom.combine(edat) { n, e ->
    "$n te $e anys"
}

Exemple practic amb un ViewModel:

class CercaViewModel : ViewModel() {
    private val _textCerca = MutableStateFlow("")
    private val _totsElsArticles = MutableStateFlow<List<Article>>(emptyList())

    val articlesFiltrats: Flow<List<Article>> = _textCerca
        .combine(_totsElsArticles) { text, articles ->
            if (text.isEmpty()) articles
            else articles.filter { it.nom.contains(text, ignoreCase = true) }
        }
}