Skip to main content

Colecciones Mutables

⏱ Dedicación recomendada: 0 minutos
Esto considera el contenido visible y relevante, e ignora texto colapsado o marcado como opcional.


r8vnhill/collections-kt

En Kotlin, las colecciones mutables son estructuras de datos que permiten modificar su contenido después de ser creadas. Son útiles cuando se requiere agregar, eliminar o actualizar elementos en tiempo de ejecución. Todas las colecciones estándar de Kotlin, como listas, conjuntos y mapas, tienen versiones mutables que proporcionan métodos adicionales para modificar su contenido.

Diferencia entre Colecciones Mutables e Inmutables

Aunque las colecciones mutables y las inmutables comparten interfaces similares, solo las mutables permiten modificar su contenido directamente.

Es importante notar que una colección mutable no necesita ser asignada a una variable var para permitir modificaciones. Aunque la referencia esté declarada como val, el contenido de la colección sigue siendo mutable. Asignar colecciones mutables a val ayuda a prevenir cambios accidentales en la referencia, lo que mejora la seguridad y la mantenibilidad del código a medida que este crece en complejidad. Sin embargo, si se intenta reasignar una colección declarada con val, se producirá un error de compilación. Por lo tanto, se recomienda usar val siempre que sea posible para garantizar un código más seguro y robusto.

MutableIterator y MutableIterable

Un MutableIterator es una interfaz que permite recorrer y modificar elementos en una colección mutable. A diferencia de un iterador estándar, un MutableIterator permite agregar y eliminar elementos durante la iteración.

interface MutableIterator<out T> : Iterator<T> {

fun remove(): Unit
}

Un MutableIterable es una interfaz que extiende Iterable y permite obtener un iterador mutable para recorrer y modificar los elementos de la colección.

interface MutableIterable<out T> : Iterable<T> {

fun iterator(): MutableIterator<T>
}

Ejemplo

val numbers: MutableIterable<Int> = 
mutableListOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val iterator = numbers.iterator()
while (iterator.hasNext()) {
if (iterator.next() % 2 == 0) {
iterator.remove()
}
}
numbers shouldBe listOf(1, 3, 5, 7, 9)

MutableCollection

Una MutableCollection es una colección iterable que permite modificar su contenido. Además de las propiedades estándar de una colección, como size y contains, las colecciones mutables incluyen métodos para agregar y eliminar elementos.

MutableCollection.kt simplificado
interface MutableCollection<E> : Collection<E>, MutableIterable<E> {
fun add(element: E): Boolean
fun remove(element: E): Boolean
fun addAll(elements: Collection<E>): Boolean
fun removeAll(elements: Collection<E>): Boolean
fun retainAll(elements: Collection<E>): Boolean
fun clear(): Unit
}

Ejemplo

val mutableStrings: MutableCollection<String> = 
mutableListOf("Kotlin", "Java")
mutableStrings += "Scala"
mutableStrings shouldBe listOf("Kotlin", "Java", "Scala")

MutableList

Una MutableList es una lista ordenada que permite modificar los elementos en una posición específica, agregar o eliminar elementos. Es útil cuando se necesita acceso directo mediante índices y operaciones de manipulación de datos.

MutableList.kt simplificado
interface MutableList<E> : List<E>, MutableCollection<E> {
fun addAll(index: Int, elements: Collection<E>): Boolean
operator fun set(index: Int, element: E): E
fun add(index: Int, element: E): Unit
fun removeAt(index: Int): E
override fun listIterator(): MutableListIterator<E>
override fun listIterator(index: Int): MutableListIterator<E>
override fun subList(fromIndex: Int, toIndex: Int): MutableList<E>
}

Ejemplo

val mutableList: MutableList<String> =
mutableListOf("Kotlin", "Java", "Scala")
mutableList[1] = "Groovy"
mutableList shouldBe listOf("Kotlin", "Groovy", "Scala")

MutableSet

Un MutableSet es una colección que no permite elementos duplicados, pero permite modificar su contenido agregando o eliminando elementos. Los sets no garantizan un orden específico de los elementos.

MutableSet.kt simplificado
interface MutableSet<E> : Set<E>, MutableCollection<E>

Ejemplo

val mutableSet: MutableSet<String> =
mutableSetOf("Kotlin", "Java")
mutableSet += "Scala"
mutableSet shouldBe setOf("Kotlin", "Java", "Scala")

MutableMap

Un MutableMap es una colección que asocia claves (keys) con valores (values), permitiendo modificar los pares clave-valor. Cada clave puede estar asociada a un solo valor, pero se pueden agregar, eliminar o modificar entradas en el mapa.

interface MutableMap<K, V> : Map<K, V> {
fun put(key: K, value: V): V?
fun remove(key: K): V?
fun remove(key: K, value: V): Boolean
fun putAll(from: Map<out K, V>): Unit
fun clear(): Unit
interface MutableEntry<K, V> : Map.Entry<K, V> {
fun setValue(newValue: V): V
}
}

Ejemplo

val mutableMap: MutableMap<String, String> =
mutableMapOf("kotlin" to "JVM")
mutableMap["java"] = "JVM"
mutableMap["scala"] = "JVM"
mutableMap shouldBe mapOf(
"kotlin" to "JVM",
"java" to "JVM",
"scala" to "JVM"
)

Ventajas y desventajas de las colecciones mutables

Beneficios

  • Flexibilidad y dinamismo: Las colecciones mutables permiten agregar, eliminar y modificar elementos en tiempo de ejecución, lo que es útil en escenarios donde los datos cambian frecuentemente o deben ser actualizados dinámicamente.
  • Interfaz consistente: Kotlin proporciona interfaces y métodos comunes para todas las colecciones mutables, facilitando el aprendizaje y el uso uniforme de estas estructuras en diferentes contextos.
  • Compatibilidad con APIs Java: Las colecciones mutables en Kotlin son interoperables con las colecciones de Java, lo que permite integrarlas sin problemas en proyectos que utilizan bibliotecas o componentes basados en Java.
  • Optimización de recursos: Al modificar directamente las colecciones sin necesidad de crear nuevas instancias, las colecciones mutables permiten optimizar el uso de memoria y mejorar el rendimiento en ciertas operaciones.

Limitaciones

  • Peligro de modificación inesperada: Las colecciones mutables pueden ser modificadas accidentalmente en diferentes partes del código, lo que puede llevar a errores difíciles de rastrear y comportamientos inesperados en tiempo de ejecución.
  • Menor seguridad y previsibilidad: En comparación con las colecciones inmutables, las mutables ofrecen menos garantías en cuanto a la inmutabilidad de los datos, lo que puede dificultar el razonamiento sobre el estado del sistema y las operaciones concurrentes.
  • Propenso a errores de sincronización: En contextos multihilo, las colecciones mutables requieren sincronización manual o uso de colecciones especiales (como ConcurrentHashMap) para evitar errores como condiciones de carrera, lo que añade complejidad al desarrollo.
  • Menor control sobre el flujo de datos: Las colecciones mutables no favorecen la programación funcional, ya que permiten cambiar el estado interno de manera libre, haciendo que sea más difícil de depurar y probar, especialmente en arquitecturas que buscan inmutabilidad y transparencia referencial.

¿Qué aprendimos?

En esta lección, exploramos las colecciones mutables en Kotlin y sus aplicaciones en la programación.

Puntos clave

  • Flexibilidad y Dinamismo: Las colecciones mutables permiten modificar su contenido en tiempo de ejecución, lo que las hace ideales para escenarios donde los datos cambian con frecuencia.
  • Interfaces Consistentes: Kotlin ofrece una interfaz uniforme para todas las colecciones mutables, lo que simplifica su uso y permite a los desarrolladores aprender a manejarlas de manera eficiente.
  • Interoperabilidad con Java: Las colecciones mutables en Kotlin se integran perfectamente con las APIs de Java, lo que facilita su uso en proyectos que combinan ambos lenguajes.
  • Riesgos y Complejidad: A pesar de sus ventajas, las colecciones mutables presentan desafíos, especialmente en entornos multihilo y en arquitecturas que favorecen la inmutabilidad. Es importante ser consciente de estas limitaciones para tomar decisiones informadas sobre cuándo y cómo utilizarlas.

Al comprender las ventajas y desventajas de las colecciones mutables, podemos tomar decisiones más estratégicas en la arquitectura y diseño de software, asegurando que el uso de estas colecciones sea apropiado para los requisitos específicos de cada proyecto.

Bibliografías Recomendadas

  • 📚 "Chapter 9. Collections: Get Organized". (2019). Dawn Griffiths & David Griffiths, en Head First Kotlin, (pp. 439-502.) O’Reilly.