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
associateBypara 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.