Kotlin: Estructuras de datos
Kotlin ofrece estructuras de datos clave para el desarrollo eficiente de aplicaciones. Descubre cómo implementar y utilizar listas, conjuntos y mapas en Kotlin para optimizar tu código.
Aprende Kotlin GRATIS y certifícateLas estructuras de datos son componentes fundamentales en la programación, permitiendo organizar y manipular datos de manera eficiente. Kotlin, como lenguaje moderno y versátil, proporciona una amplia variedad de estructuras de datos que facilitan el desarrollo de aplicaciones robustas. En este artículo, exploraremos las principales estructuras de datos en Kotlin y cómo utilizarlas correctamente.
Listas en Kotlin
Las listas son colecciones ordenadas que permiten almacenar elementos en una secuencia. Kotlin ofrece dos tipos principales de listas: listas inmutables y listas mutables.
Listas inmutables
Las listas inmutables no permiten modificar sus elementos después de la creación. Se crean utilizando la función listOf()
.
val numeros = listOf(1, 2, 3, 4, 5)
Características de las listas inmutables:
- Lectura segura: al ser inmutables, no hay riesgo de modificar accidentalmente los elementos.
- Uso compartido: se pueden compartir entre diferentes partes del programa sin preocuparse por modificaciones.
Operaciones con listas inmutables
Aunque no se pueden modificar, es posible realizar varias operaciones que devuelven nuevas listas:
- Añadir elementos:
val nuevosNumeros = numeros + 6
- Eliminar elementos:
val menosNumeros = numeros - 1
Listas mutables
Las listas mutables permiten modificar su contenido después de la creación. Se crean utilizando la función mutableListOf()
.
val frutas = mutableListOf("Manzana", "Banana", "Cereza")
frutas.add("Durazno")
Operaciones habituales:
- Agregar elementos:
frutas.add("Mango")
- Eliminar elementos:
frutas.remove("Banana")
- Modificar elementos:
frutas[0] = "Fresa"
Conjuntos en Kotlin
Los conjuntos son colecciones que no permiten elementos duplicados. Son útiles cuando se necesita garantizar la unicidad de los elementos.
Conjuntos inmutables
Se crean utilizando setOf()
.
val colores = setOf("Rojo", "Verde", "Azul")
Características:
- Sin duplicados: añadir un elemento ya existente no cambia el conjunto.
- Orden no garantizado: el orden de los elementos puede no ser el mismo al iterar.
Conjuntos mutables
Se crean con mutableSetOf()
.
val numerosSet = mutableSetOf(1, 2, 3)
numerosSet.add(4)
Operaciones comunes:
- Agregar elementos:
numerosSet.add(2) // No se agregará ya que 2 ya existe
- Eliminar elementos:
numerosSet.remove(3)
Mapas en Kotlin
Los mapas almacenan pares clave-valor, permitiendo asociar valores a claves únicas.
Mapas inmutables
Se crean con mapOf()
.
val capitales = mapOf("España" to "Madrid", "Francia" to "París")
Características:
- Claves únicas: no puede haber claves duplicadas.
- Valores asociados: cada clave tiene un valor correspondiente.
Mapas mutables
Se crean con mutableMapOf()
.
val edades = mutableMapOf("Juan" to 30, "María" to 25)
edades["Pedro"] = 40
Operaciones habituales:
- Agregar o modificar entradas:
edades["María"] = 26 // Modifica el valor existente
- Eliminar entradas:
edades.remove("Juan")
- Acceder a valores:
val edadDeMaría = edades["María"]
Colecciones y nullabilidad
Kotlin maneja la nullabilidad de forma segura, lo que se extiende a las colecciones.
Listas que permiten nulos
Es posible crear listas que contengan elementos nulos:
val listaConNulos = listOf("A", null, "B")
Para manejar nulos al iterar:
for (elemento in listaConNulos) {
elemento?.let {
println(it)
}
}
Operaciones funcionales en colecciones
Kotlin incorpora funciones de estilo funcional para manipular colecciones de manera concisa y expresiva.
Transformaciones con map
La función map
transforma cada elemento de una colección y devuelve una nueva colección.
val numeros = listOf(1, 2, 3)
val cuadrados = numeros.map { it * it } // [1, 4, 9]
Filtrado con filter
La función filter
selecciona elementos que cumplen una condición.
val numerosPares = numeros.filter { it % 2 == 0 } // [2]
Combinación de operaciones
Se pueden encadenar múltiples operaciones:
val resultado = numeros
.filter { it > 1 }
.map { it * 2 }
.sortedDescending()
Secuencias para procesamiento perezoso
Las secuencias diferencian de las colecciones en que realizan el procesamiento de forma perezosa, lo cual puede ser más eficiente con grandes cantidades de datos.
Creación de secuencias
Se puede convertir una colección en secuencia:
val numerosSecuencia = numeros.asSequence()
O crear una secuencia desde cero:
val secuenciaInfinita = generateSequence(0) { it + 1 }
Uso eficiente de secuencias
Las secuencias son especialmente útiles cuando se encadenan muchas operaciones:
val primerosDiezMil = secuenciaInfinita
.filter { it % 2 == 0 }
.map { it * it }
.take(10000)
.toList()
En este caso, las operaciones se realizan solo sobre los primeros 10,000 elementos necesarios.
Estructuras de datos personalizadas
Kotlin permite definir estructuras de datos propias mediante clases y otros mecanismos.
Clases de datos
Las clases de datos (data class
) son ideales para almacenar información:
data class Producto(val nombre: String, val precio: Double)
Uso en colecciones
Se pueden crear colecciones de objetos:
val productos = listOf(
Producto("Ordenador", 999.99),
Producto("Teléfono", 499.49),
Producto("Tablet", 299.99)
)
Operaciones avanzadas
- Suma de valores:
val precioTotal = productos.sumOf { it.precio }
- Encontrar un elemento:
val productoCaro = productos.find { it.precio > 500 }
- Agrupar elementos:
val productosPorPrecio = productos.groupBy {
when {
it.precio < 500 -> "Barato"
else -> "Caro"
}
}
Inmutabilidad y concurrencia
La inmutabilidad es especialmente beneficiosa en entornos concurrentes.
Colecciones inmutables y hilos
Al utilizar colecciones inmutables, se evita la necesidad de sincronización explícita:
val datosCompartidos = listOf(1, 2, 3)
// Acceso seguro desde múltiples hilos
Colecciones en coroutines
Kotlin ofrece soporte para programación asíncrona mediante coroutines, lo que impacta en el uso de colecciones.
Flows y recolección de datos
Los Flow
permiten manejar flujos de datos asíncronos.
import kotlinx.coroutines.flow.*
val flujoDatos = flow {
for (i in 1..5) {
emit(i)
}
}
flujoDatos.collect { valor ->
println(valor)
}
Transformaciones en Flows
Se pueden aplicar operaciones similares a las colecciones:
flujoDatos
.filter { it % 2 == 0 }
.map { it * it }
.collect { println(it) }
Buenas prácticas con colecciones en Kotlin
- Preferir inmutabilidad: usar colecciones inmutables por defecto.
- Elegir la estructura adecuada: seleccionar entre listas, conjuntos o mapas según las necesidades.
- Utilizar operaciones funcionales: aprovechar
map
,filter
,reduce
, etc., para escribir código conciso. - Considerar secuencias: usar secuencias para procesamiento de grandes datasets.
- Gestionar la nullabilidad: prestar atención a elementos nulos en colecciones.
Lecciones de este módulo de Kotlin
Lecciones de programación del módulo Estructuras de datos del curso de Kotlin.
Ejercicios de programación en este módulo de Kotlin
Evalúa tus conocimientos en Estructuras de datos con ejercicios de programación Estructuras de datos de tipo Test, Puzzle, Código y Proyecto con VSCode.