Kotlin
Tutorial Kotlin: Listas, Conjuntos y Mapas
Explora en Kotlin el uso de listas, conjuntos y mapas, aprendiendo sus características inmutables y mutables, y técnicas para su manipulación eficiente.
Aprende Kotlin GRATIS y certifícateDefinición y uso de listas (List
, MutableList
)
En Kotlin, una lista es una colección ordenada de elementos que permite acceso indexado. Existen dos tipos principales: List
, que es inmutable, y MutableList
, que puede ser modificada después de su creación.
La interfaz List<T>
representa una colección de elementos de solo lectura. Una vez creada, no es posible añadir ni eliminar elementos. Para crear una lista inmutable, se utiliza la función listOf()
:
val numeros: List<Int> = listOf(1, 2, 3, 4, 5)
En este ejemplo, numeros
es una lista inmutable de enteros. Intentar modificarla resultará en un error de compilación, garantizando la inmutabilidad de la colección.
Por otro lado, MutableList<T>
es una interfaz que permite modificar su contenido. Para crear una lista mutable, se utiliza la función mutableListOf()
:
val letras: MutableList<Char> = mutableListOf('a', 'b', 'c')
Con letras
, es posible añadir, eliminar o actualizar elementos según sea necesario:
letras.add('d') // Añade 'd' al final de la lista
letras.remove('b') // Elimina 'b' de la lista
letras[0] = 'z' // Reemplaza el elemento en la posición 0 por 'z'
El acceso a los elementos de una lista se realiza mediante índices, comenzando desde cero. Para obtener un elemento específico, se utiliza la sintaxis de corchetes:
val primerNumero = numeros[0] // Accede al primer elemento de 'numeros'
Es importante manejar adecuadamente los índices para evitar excepciones como IndexOutOfBoundsException
.
Las listas en Kotlin también proporcionan diversas funciones para facilitar su manipulación. Por ejemplo, para verificar si un elemento existe en la lista, se puede usar contains
:
if ('a' in letras) {
println("La lista contiene la letra 'a'")
}
Para conocer el tamaño de la lista, se utiliza la propiedad size
:
println("La lista tiene ${letras.size} elementos")
Es posible crear listas vacías cuando no se conocen los elementos de antemano. Para una lista inmutable vacía:
val listaVacia: List<String> = listOf()
Y para una lista mutable vacía:
val listaMutableVacia: MutableList<String> = mutableListOf()
Al trabajar con listas, es común iterar sobre sus elementos utilizando bucles. El bucle for
es especialmente útil para este propósito:
for (letra in letras) {
println(letra)
}
Este código recorrerá cada elemento de letras
y lo imprimirá en pantalla.
Otra característica esencial es la posibilidad de combinar diferentes tipos en una lista mediante el uso de tipos genéricos o la clase Any
. Sin embargo, es recomendable especificar el tipo de datos para mantener la seguridad de tipo y evitar errores en tiempo de ejecución.
val mixto: List<Any> = listOf(1, "dos", 3.0)
Aunque es posible, se debe tener cuidado al manejar listas con elementos de diferentes tipos.
Finalmente, es relevante mencionar que, aunque las listas inmutables no pueden ser modificadas, es posible crear una nueva lista basada en una existente utilizando funciones como plus
:
val nuevaLista = numeros.plus(6) // Crea una nueva lista con el elemento '6' añadido
De esta manera, se mantiene la inmutabilidad de la lista original mientras se genera una versión ampliada.
Definición y uso de conjuntos (Set
, MutableSet
)
En Kotlin, un conjunto es una colección de elementos únicos que no mantiene un orden específico. Existen dos tipos principales: Set
, que es inmutable, y MutableSet
, que permite modificaciones después de su creación.
La interfaz Set<T>
representa una colección de solo lectura donde cada elemento es único. Para crear un conjunto inmutable, se utiliza la función setOf()
:
val numeros: Set<Int> = setOf(1, 2, 3, 4, 5)
En este ejemplo, numeros
es un conjunto inmutable de enteros. Los elementos duplicados se ignoran, asegurando la unicidad dentro del conjunto.
Si se necesita un conjunto mutable, capaz de añadir o eliminar elementos, se emplea la función mutableSetOf()
:
val letras: MutableSet<Char> = mutableSetOf('a', 'b', 'c')
Con letras
, es posible modificar el contenido del conjunto de la siguiente manera:
letras.add('d') // Añade 'd' al conjunto
letras.remove('b') // Elimina 'b' del conjunto
Es importante destacar que, al añadir elementos repetidos en un conjunto, estos no se duplicarán. Por ejemplo:
letras.add('a') // 'a' ya existe en el conjunto, no se añade de nuevo
Para verificar si un elemento específico pertenece al conjunto, se utiliza la expresión in
:
if ('c' in letras) {
println("El conjunto contiene la letra 'c'")
}
Los conjuntos no tienen un orden predefinido, por lo que al iterar sobre ellos, el orden de los elementos puede no ser el mismo que el de inserción. Para recorrer un conjunto, se puede utilizar un bucle for
:
for (letra in letras) {
println(letra)
}
La propiedad size
permite conocer el tamaño del conjunto, es decir, el número de elementos únicos que contiene:
println("El conjunto tiene ${letras.size} elementos")
Para crear conjuntos vacíos, se especifica el tipo genérico de la siguiente manera:
val conjuntoVacio: Set<String> = setOf()
val conjuntoMutableVacio: MutableSet<String> = mutableSetOf()
Es posible realizar operaciones de conjuntos como unión, intersección y diferencia utilizando funciones integradas. Por ejemplo:
val numerosPares = setOf(2, 4, 6)
val numerosImpares = setOf(1, 3, 5)
val union = numerosPares.union(numerosImpares) // Unión de dos conjuntos
val interseccion = numerosPares.intersect(numerosImpares) // Intersección (vacía en este caso)
Estas operaciones facilitan el manejo de colecciones de elementos únicos en distintas situaciones.
Para convertir otros tipos de colecciones en conjuntos y eliminar elementos duplicados, se puede utilizar la función toSet()
:
val listaConDuplicados = listOf(1, 2, 2, 3, 3, 3)
val conjuntoSinDuplicados = listaConDuplicados.toSet() // Resultado: {1, 2, 3}
Los conjuntos son especialmente útiles cuando se requiere garantizar que no haya elementos repetidos y el orden de los elementos no es relevante.
Finalmente, aunque Set
es inmutable, es posible crear nuevos conjuntos basados en otros utilizando operaciones como plus
o minus
:
val nuevoConjunto = numeros.plus(6) // Añade '6', creando un nuevo conjunto
val conjuntoReducido = numeros.minus(1) // Elimina '1', creando un nuevo conjunto
De esta forma, se mantiene la inmutabilidad del conjunto original mientras se generan nuevas versiones modificadas.
Definición y uso de mapas (Map
, MutableMap
)
En Kotlin, un mapa es una colección que asocia claves únicas con valores. Es ideal para almacenar pares clave-valor donde cada clave permite acceder a su valor correspondiente. Existen dos tipos principales de mapas: Map
, que es inmutable, y MutableMap
, que permite modificaciones después de su creación.
La interfaz Map<K, V>
representa un mapa de solo lectura. Una vez creado, no es posible añadir ni eliminar entradas. Para crear un mapa inmutable, se utiliza la función mapOf()
:
val capitales: Map<String, String> = mapOf(
"España" to "Madrid",
"Francia" to "París",
"Italia" to "Roma"
)
En este ejemplo, capitales
es un mapa inmutable que relaciona países con sus capitales. La sintaxis "clave" to "valor"
permite definir cada par clave-valor de forma clara.
Si se necesita un mapa que pueda ser modificado, se emplea la función mutableMapOf()
para crear un MutableMap
:
val edades: MutableMap<String, Int> = mutableMapOf(
"Ana" to 28,
"Luis" to 34,
"María" to 22
)
Con edades
, es posible añadir, actualizar o eliminar entradas según sea necesario:
edades["Carlos"] = 30 // Añade una nueva entrada al mapa
edades["Luis"] = 35 // Actualiza el valor asociado a la clave "Luis"
edades.remove("Ana") // Elimina la entrada con la clave "Ana"
Para acceder al valor asociado a una clave específica, se utiliza la sintaxis de corchetes o el método get()
:
val edadMaria = edades["María"] // Devuelve 22
val edadLuis = edades.get("Luis") // Devuelve 35
Es importante tener en cuenta que si se intenta acceder a una clave que no existe, el resultado será null
. Para manejar esto de manera segura, se puede utilizar el operador elvis ?:
o el método getValue()
que arroja una excepción si la clave no está presente:
val edadCarlos = edades["Carlos"] ?: 0 // Devuelve 30 si existe, 0 si no
Para comprobar si una clave o un valor existe en el mapa, se utilizan los métodos containsKey()
y containsValue()
:
if (edades.containsKey("María")) {
println("El mapa contiene la clave 'María'")
}
if (edades.containsValue(34)) {
println("Alguien tiene 34 años")
}
La propiedad size
permite conocer el número de entradas en el mapa:
println("El mapa tiene ${edades.size} entradas")
Es posible iterar sobre los mapas utilizando bucles for
. Se pueden recorrer las claves, los valores o ambos:
for ((nombre, edad) in edades) {
println("$nombre tiene $edad años")
}
for (nombre in edades.keys) {
println("Clave: $nombre")
}
for (edad in edades.values) {
println("Valor: $edad")
}
Al crear mapas vacíos, es necesario especificar los tipos genéricos:
val mapaVacio: Map<Int, String> = mapOf()
val mapaMutableVacio: MutableMap<Int, String> = mutableMapOf()
Los mapas soportan diversas operaciones útiles. Por ejemplo, para combinar dos mapas y crear uno nuevo:
val mapa1 = mapOf(1 to "uno", 2 to "dos")
val mapa2 = mapOf(3 to "tres", 4 to "cuatro")
val mapaCombinado = mapa1 + mapa2 // Combina ambos mapas
Para eliminar entradas de un mapa inmutable y crear uno nuevo sin ciertas claves:
val mapaReducido = mapaCombinado - 1 // Elimina la entrada con clave 1
En mapas mutables, es posible utilizar métodos como putAll()
para añadir múltiples entradas:
val nuevosDatos = mapOf("Pedro" to 29, "Luisa" to 25)
edades.putAll(nuevosDatos)
Si se necesita realizar una acción específica cuando se accede a una clave ausente, se puede utilizar el método getOrDefault()
:
val edadJuan = edades.getOrDefault("Juan", 0) // Devuelve 0 si "Juan" no está en el mapa
Es posible convertir un mapa en otras colecciones. Por ejemplo, para obtener una lista de pares clave-valor:
val listaDePares = edades.toList() // Devuelve una lista de Pair<String, Int>
O para convertir claves y valores en listas separadas:
val listaDeNombres = edades.keys.toList()
val listaDeEdades = edades.values.toList()
Cuando se trabaja con mapas, es fundamental recordar que las claves deben ser únicas. Si se añade una entrada con una clave existente en un MutableMap
, el valor anterior será reemplazado
Para evitar sobrescribir valores, se puede comprobar previamente si la clave existe:
if ("Luisa" !in edades) {
edades["Luisa"] = 25
}
Los mapas son altamente versátiles y se utilizan en numerosas situaciones donde se necesita una asociación entre claves y valores. Comprender su uso y funciones disponibles es esencial para manejar colecciones en Kotlin de manera eficiente.
Operaciones comunes sobre colecciones (filtrar, mapear, agrupar)
Las colecciones en Kotlin ofrecen funciones de orden superior que facilitan su manipulación y transformación. Entre las operaciones más utilizadas se encuentran filtrar, mapear y agrupar, las cuales permiten procesar y reorganizar los datos de manera eficiente.
El filtrado consiste en obtener elementos que cumplen una condición específica. La función filter
devuelve una nueva colección con los elementos que satisfacen un predicado dado:
val numeros = listOf(1, 2, 3, 4, 5, 6)
val numerosPares = numeros.filter { it % 2 == 0 } // Resultado: [2, 4, 6]
En este ejemplo, numerosPares
contiene solo los números que son divisibles por 2. Es posible utilizar filterNot
para obtener los elementos que no cumplen la condición:
val numerosImpares = numeros.filterNot { it % 2 == 0 } // Resultado: [1, 3, 5]
Para trabajar con listas que pueden contener valores nulos, se emplea filterNotNull
, que elimina los elementos nulos de la colección:
val listaConNulos = listOf(1, null, 2, null, 3)
val listaSinNulos = listaConNulos.filterNotNull() // Resultado: [1, 2, 3]
El mapeo transforma cada elemento de una colección aplicando una función y devuelve una nueva colección con los resultados. La función map
es esencial para esta operación:
val cuadrados = numeros.map { it * it } // Resultado: [1, 4, 9, 16, 25, 36]
Aquí, cada número en numeros
se eleva al cuadrado. Si se desea transformar y a la vez filtrar elementos nulos o realizar una transformación que pueda devolver nulos, se utiliza mapNotNull
:
val cadenas = listOf("1", "2", "a", "3")
val numerosEnteros = cadenas.mapNotNull { it.toIntOrNull() } // Resultado: [1, 2, 3]
En este caso, toIntOrNull()
intenta convertir cada cadena a entero y devuelve null
si falla. mapNotNull
excluye esos nulos del resultado final.
La agrupación organiza los elementos de una colección en un mapa según un criterio especificado. La función groupBy
es fundamental para esta tarea:
data class Persona(val nombre: String, val edad: Int)
val personas = listOf(
Persona("Ana", 28),
Persona("Luis", 34),
Persona("María", 28),
Persona("Pedro", 34)
)
val personasPorEdad = personas.groupBy { it.edad }
// Resultado: {28=[Persona(nombre=Ana, edad=28), Persona(nombre=María, edad=28)], 34=[Persona(nombre=Luis, edad=34), Persona(nombre=Pedro, edad=34)]}
En el ejemplo anterior, las personas se han agrupado por edad, creando un mapa donde cada clave es una edad y el valor es una lista de personas con esa edad.
Además, es posible transformar los elementos al agrupar utilizando groupBy
con dos parámetros:
val nombresPorEdad = personas.groupBy(
keySelector = { it.edad },
valueTransform = { it.nombre }
)
// Resultado: {28=["Ana", "María"], 34=["Luis", "Pedro"]}
Así, se obtiene un mapa que asocia edades con los nombres de las personas que tienen esa edad.
Otra operación útil es associateBy
, que crea un mapa a partir de una colección, donde las claves se definen mediante una función y los valores son los elementos originales:
val personasPorNombre = personas.associateBy { it.nombre }
// Resultado: {"Ana"=Persona(nombre=Ana, edad=28), "Luis"=Persona(nombre=Luis, edad=34), ...}
Si se desea modificar los valores del mapa, se puede utilizar associateBy
con un parámetro adicional:
val edadesPorNombre = personas.associateBy(
keySelector = { it.nombre },
valueTransform = { it.edad }
)
// Resultado: {"Ana"=28, "Luis"=34, "María"=28, "Pedro"=34}
La función partition
divide una colección en dos listas según un predicado. Es útil cuando se necesita separar elementos que cumplen una condición de los que no:
val (menoresDe30, mayoresDe30) = personas.partition { it.edad < 30 }
// menoresDe30: [Persona(nombre=Ana, edad=28), Persona(nombre=María, edad=28)]
// mayoresDe30: [Persona(nombre=Luis, edad=34), Persona(nombre=Pedro, edad=34)]
Con flatMap
, se puede transformar cada elemento de una colección en una colección y luego concatenarlas en una sola. Es especialmente útil para manipular colecciones anidadas:
val listasDeNumeros = listOf(listOf(1, 2), listOf(3, 4), listOf(5, 6))
val todosLosNumeros = listasDeNumeros.flatMap { it } // Resultado: [1, 2, 3, 4, 5, 6]
El uso de estas operaciones optimiza el procesamiento de datos en Kotlin, permitiendo escribir código más conciso y legible. Es importante familiarizarse con ellas para aprovechar al máximo las capacidades de las colecciones en este lenguaje.
Ejercicios de esta lección Listas, Conjuntos y Mapas
Evalúa tus conocimientos de esta lección Listas, Conjuntos y Mapas con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Clases genéricas con varianza y restricciones
Introducción a las corutinas
Uso de asincronía con suspend, async y await
Formateo de cadenas texto
Uso de monads y manejo funcional de errores
Declaración y uso de variables y constantes
Uso de la concurrencia funcional con corutinas
Operaciones en colecciones
Uso de clases y objetos en Kotlin
Evaluación Kotlin
Funciones de orden superior y expresiones lambda en Kotlin
Herencia y polimorfismo en Kotlin
Inmutabilidad y datos inmutables
Uso de funciones parciales y currificaciones
Primer programa en Kotlin
Introducción a la programación funcional
Introducción a Kotlin
Uso de operadores y expresiones
Sistema de inventario de tienda
Uso de data classes y destructuring
Composición de funciones
Uso de interfaces y clases abstractas
Simulador de conversión de monedas
Programación funcional y concurrencia
Creación y uso de listas, conjuntos y mapas
Transformación en monads y functors
Crear e invocar funciones
Uso de las estructuras de control
Todas las lecciones de Kotlin
Accede a todas las lecciones de Kotlin y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Kotlin
Introducción Y Entorno
Instalación Y Primer Programa De Kotlin
Introducción Y Entorno
Tipos De Datos, Variables Y Constantes
Sintaxis
Operadores Y Expresiones
Sintaxis
Cadenas De Texto Y Manipulación
Sintaxis
Estructuras De Control
Sintaxis
Funciones Y Llamada De Funciones
Sintaxis
Clases Y Objetos
Programación Orientada A Objetos
Herencia Y Polimorfismo
Programación Orientada A Objetos
Interfaces Y Clases Abstractas
Programación Orientada A Objetos
Data Classes Y Destructuring
Programación Orientada A Objetos
Tipos Genéricos Y Varianza
Programación Orientada A Objetos
Listas, Conjuntos Y Mapas
Estructuras De Datos
Introducción A La Programación Funcional
Programación Funcional
Funciones De Primera Clase Y De Orden Superior
Programación Funcional
Inmutabilidad Y Datos Inmutables
Programación Funcional
Composición De Funciones
Programación Funcional
Monads Y Manejo Funcional De Errores
Programación Funcional
Operaciones Funcionales En Colecciones
Programación Funcional
Transformaciones En Monads Y Functors
Programación Funcional
Funciones Parciales Y Currificación
Programación Funcional
Introducción A Las Corutinas
Coroutines Y Asincronía
Asincronía Con Suspend, Async Y Await
Coroutines Y Asincronía
Concurrencia Funcional
Coroutines Y Asincronía
Evaluación
Evaluación
Certificados de superación de Kotlin
Supera todos los ejercicios de programación del curso de Kotlin y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender la diferencia entre
List
yMutableList
. - Conocer cómo crear y manipular conjuntos con
Set
yMutableSet
. - Aprender a utilizar
Map
yMutableMap
para gestionar pares clave-valor. - Aplicar operaciones de transformación como filtrado, mapeo y agrupación sobre colecciones.