Las estructuras de datos no solo se diferencian por la forma en que almacenan elementos, sino también por cómo evolucionan cuando cambian los datos. Conocer la distinción entre estructuras mutables, inmutables y persistentes permite elegir la herramienta correcta al diseñar APIs, coordinar hilos o gestionar historiales.
En esta sección presentamos los conceptos fundamentales, analizamos sus implicaciones en memoria y rendimiento, y repasamos escenarios reales donde cada enfoque ofrece ventajas concretas.
8. Mutabilidad y persistencia
Mutabilidad describe la capacidad de modificar una estructura existente; persistencia indica si los estados previos permanecen accesibles tras los cambios. A continuación detallamos las variaciones más comunes.
8.1 Estructuras mutables
Las estructuras mutables permiten alterar su contenido directamente: insertar, eliminar o actualizar valores modifica el mismo bloque de memoria. Ejemplos habituales incluyen arrays redimensionables, tablas hash tradicionales y la mayoría de colecciones de lenguajes imperativos.
- Ventajas: rendimiento predecible; no es necesario asignar nuevos objetos para cada cambio y se aprovecha la localidad de caché.
- Responsabilidades: se debe controlar la concurrencia mediante bloqueos o estructuras lock-free para evitar condiciones de carrera.
- Buenas prácticas: documentar quién es el dueño de cada referencia y evitar exponer punteros internos que puedan invalidarse tras una realocación.
8.2 Estructuras inmutables
Una estructura inmutable no cambia una vez creada; cualquier operación devuelve una copia modificada. Lenguajes funcionales como Clojure promueven este enfoque para simplificar el razonamiento y la concurrencia.
- Beneficios: referencias seguras, ausencia de efectos secundarios y facilidad para realizar backtracking o depuración paso a paso.
- Costos: incrementan el uso de memoria y generan presión adicional en el recolector de basura si se crean muchas copias superficiales.
- Optimiza: compartir subestructuras cuando sea posible (por ejemplo, listas que reutilizan nodos iniciales) minimiza la duplicación real.
8.3 Estructuras persistentes
Una estructura persistente conserva todos los estados anteriores y permite acceder a ellos aun después de aplicar cambios. Muchas estructuras inmutables son intrínsecamente persistentes porque cada versión sigue disponible, pero también existen implementaciones mutables con persistencia parcial (solo algunos estados se preservan).
- Perfiles típicos: editores con deshacer ilimitado, motores de reglas que necesitan conocer la historia de un dato y sistemas de replicación.
- Técnicas: usar árboles balanceados persistentes (como red-black) o copiar solo los nodos afectados para construir versiones nuevas sin reescribir todo.
- Recolección: es clave liberar versiones obsoletas o implementar contadores de referencias para evitar que la memoria crezca indefinidamente.
8.4 Ventajas y desventajas
Elegir entre mutabilidad e inmutabilidad depende de compromisos concretos. Lo siguiente resume los factores a sopesar:
- Rendimiento vs seguridad: las estructuras mutables suelen ser más rápidas y eficientes en memoria, pero también más vulnerables a estados inconsistentes.
- Concurrencia: las inmutables y persistentes simplifican la sincronización porque varios hilos pueden compartir versiones sin coordinar escrituras.
- Consumo de recursos: la mutabilidad concentra los cambios en un solo objeto, mientras que la persistencia eleva los requisitos de almacenamiento y puede requerir estrategias adicionales (snapshots, compresión, GC incremental).
- Modelado del dominio: sistemas financieros o de auditoría suelen preferir registros persistentes; juegos en tiempo real priorizan mutabilidad para reaccionar rápido.
La clave es combinar ambos paradigmas: emplear estructuras mutables en capas internas de alto rendimiento y ofrecer interfaces inmutables a los consumidores o servicios externos. Así se logra equilibrio entre eficiencia y previsibilidad.