Volver a Kotlin Básico

Introducción

Kotlin proporciona APIs de colecciones ricas con muchas funciones de utilidad que hacen trabajar con datos más fácil y expresivo que en Java.

Listas

Lista Inmutable:

val numeros = listOf(1, 2, 3, 4, 5) val nombres = listOf("Alicia", "Bob", "Carlos") // Acceder elementos println(numeros[0]) // 1 println(numeros.first()) // 1 println(numeros.last()) // 5 println(numeros.getOrNull(10)) // null // Verificar existencia println(2 in numeros) // true println(numeros.contains(6)) // false

Lista Mutable:

val listaMutable = mutableListOf(1, 2, 3) listaMutable.add(4) listaMutable.remove(2) listaMutable[0] = 10 println(listaMutable) // [10, 3, 4]

Conjuntos (Set)

val numerosUnicos = setOf(1, 2, 2, 3, 3, 3) println(numerosUnicos) // [1, 2, 3] val conjuntoMutable = mutableSetOf("a", "b") conjuntoMutable.add("c") conjuntoMutable.remove("a")

Mapas (Map)

val edades = mapOf( "Alicia" to 25, "Bob" to 30, "Carlos" to 35 ) // Acceso println(edades["Alicia"]) // 25 println(edades.get("David")) // null println(edades.getValue("Alicia")) // 25 (lanza excepción si no existe) println(edades.getOrDefault("David", 0)) // 0 // Mapa mutable val mapaMutable = mutableMapOf("a" to 1) mapaMutable["b"] = 2 mapaMutable.put("c", 3) mapaMutable.remove("a")

Operaciones Comunes

Filtrar (filter):

val numeros = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) val pares = numeros.filter { it % 2 == 0 } // [2, 4, 6, 8, 10] val impares = numeros.filterNot { it % 2 == 0 } // [1, 3, 5, 7, 9] val grandes = numeros.filter { it > 5 } // [6, 7, 8, 9, 10]

Transformar (map):

val numeros = listOf(1, 2, 3, 4, 5) val duplicados = numeros.map { it * 2 } // [2, 4, 6, 8, 10] val cadenas = numeros.map { "Número $it" } // Aplanar colecciones anidadas val anidada = listOf(listOf(1, 2), listOf(3, 4)) val plana = anidada.flatten() // [1, 2, 3, 4] // FlatMap val palabras = listOf("hola", "mundo") val letras = palabras.flatMap { it.toList() } // [h, o, l, a, m, u, n, d, o]

Reduce y Fold:

val numeros = listOf(1, 2, 3, 4, 5) val suma = numeros.reduce { acc, n -> acc + n } // 15 val sumaConInicial = numeros.fold(0) { acc, n -> acc + n } // 15 // Ejemplo práctico val producto = numeros.fold(1) { acc, n -> acc * n } // 120

Agrupar (groupBy):

val palabras = listOf("manzana", "banana", "albaricoque", "mora", "cereza") val porPrimeraLetra = palabras.groupBy { it.first() } // {m=[manzana, mora], b=[banana], a=[albaricoque], c=[cereza]} val porLongitud = palabras.groupBy { it.length } // {7=[manzana], 6=[banana, cereza], 11=[albaricoque], 4=[mora]}

Particionar (partition):

val numeros = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) val (pares, impares) = numeros.partition { it % 2 == 0 } println(pares) // [2, 4, 6, 8, 10] println(impares) // [1, 3, 5, 7, 9]

Ordenamiento (sorted):

val numeros = listOf(3, 1, 4, 1, 5, 9, 2, 6) val ordenados = numeros.sorted() // [1, 1, 2, 3, 4, 5, 6, 9] val descendente = numeros.sortedDescending() // [9, 6, 5, 4, 3, 2, 1, 1] val nombres = listOf("Carlos", "Alicia", "Bob") val nombresPorLongitud = nombres.sortedBy { it.length } // [Bob, Alicia, Carlos]

Elementos únicos (distinct):

val numeros = listOf(1, 2, 2, 3, 3, 3, 4) val unicos = numeros.distinct() // [1, 2, 3, 4] data class Persona(val nombre: String, val edad: Int) val personas = listOf( Persona("Alicia", 25), Persona("Bob", 30), Persona("Alicia", 35) ) val nombresUnicos = personas.distinctBy { it.nombre } // Solo primera Alicia

Tomar y Eliminar (take/drop):

val numeros = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) val primeros3 = numeros.take(3) // [1, 2, 3] val ultimos3 = numeros.takeLast(3) // [8, 9, 10] val sinPrimeros3 = numeros.drop(3) // [4, 5, 6, 7, 8, 9, 10] val menoresQue5 = numeros.takeWhile { it < 5 } // [1, 2, 3, 4]

Dividir en trozos (chunked):

val numeros = (1..10).toList() val trozos = numeros.chunked(3) // [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]] val trozosSumados = numeros.chunked(3) { it.sum() } // [6, 15, 24, 10]

Combinar (zip):

val nombres = listOf("Alicia", "Bob", "Carlos") val edades = listOf(25, 30, 35) val personas = nombres.zip(edades) // [(Alicia, 25), (Bob, 30), (Carlos, 35)] val formateado = nombres.zip(edades) { nombre, edad -> "$nombre tiene $edad años" } // [Alicia tiene 25 años, Bob tiene 30 años, Carlos tiene 35 años]

Secuencias

Para mejor rendimiento con colecciones grandes:

val numeros = (1..1_000_000).toList() // Lista (evaluación eager) val resultado1 = numeros .filter { it % 2 == 0 } .map { it * 2 } .take(10) .toList() // Secuencia (evaluación lazy) val resultado2 = numeros.asSequence() .filter { it % 2 == 0 } .map { it * 2 } .take(10) .toList()

Ejemplos Prácticos

Procesamiento de datos:

data class Jugador(val nombre: String, val puntaje: Int, val nivel: Int) val jugadores = listOf( Jugador("Alicia", 1500, 10), Jugador("Bob", 2000, 15), Jugador("Carlos", 1200, 8), Jugador("David", 1800, 12) ) // Top 2 jugadores por puntaje val mejoresJugadores = jugadores .sortedByDescending { it.puntaje } .take(2) // Promedio de puntaje de jugadores de alto nivel val puntajePromedio = jugadores .filter { it.nivel >= 10 } .map { it.puntaje } .average() // Agrupar por rango de nivel val porNivel = jugadores.groupBy { when { it.nivel < 10 -> "Principiante" it.nivel < 15 -> "Intermedio" else -> "Avanzado" } }

Procesamiento de texto:

val texto = "Hola Mundo! Esto es Kotlin." val frecuenciaPalabras = texto .lowercase() .split(" ") .filter { it.isNotBlank() } .groupingBy { it } .eachCount()

Buenas Prácticas

  • Usa colecciones inmutables por defecto
  • Usa secuencias para colecciones grandes o múltiples operaciones
  • Prefiere funciones de colección sobre bucles manuales
  • Usa filterNotNull() para eliminar nulos
  • Usa associateBy para convertir lista a mapa
  • Encadena operaciones para legibilidad

Conclusión

Las APIs de colecciones de Kotlin proporcionan formas poderosas y expresivas de manipular datos. Comprender estas operaciones te ayuda a escribir código más limpio y eficiente.

En el próximo capítulo, profundizaremos en lambdas y funciones de orden superior.