Estructuras de datos especializadas en Dart

Implementaciones especializadas con dart:collection

dart:collection es la librería que proporciona implementaciones específicas de colecciones para cuando la interfaz estándar (List, Set o Map) no es suficiente para tus requisitos de rendimiento o comportamiento. Por defecto, los literales de Dart crean versiones que preservan el orden de inserción (LinkedHashMap y LinkedHashSet), lo que añade un pequeño coste de memoria para mantener la cadena de nodos que rastrea el orden. Sin embargo, si la prioridad es la velocidad de búsqueda y no el orden, HashMap es la opción optimizada. Si necesitas una estructura para procesar tareas donde añadir o quitar elementos de ambos extremos sea una operación constante $O(1)$, debes usar Queue (que utiliza ListQueue como implementación por defecto, empleando un buffer circular). Si necesitas que tus datos se mantengan siempre ordenados por su clave, SplayTreeMap [disponible desde Dart 1.0] es la herramienta ideal. Elegir mal la implementación puede transformar una operación eficiente en un cuello de botella: usar List.removeAt(0) para simular una cola es un error de rendimiento catastrófico, y confiar en el orden de un HashMap provocará bugs lógicos impredecibles.

import 'dart:collection';

void main() {
  // 1. Uso de Queue para una cola de procesamiento de mensajes (FIFO)
  // ListQueue es la implementación por defecto; usa un buffer circular
  // para asegurar que addFirst y removeFirst sean O(1).
  final messageQueue = Queue<String>();
  messageQueue.addLast('Mensaje estándar: Hola');
  messageQueue.addFirst('Mensaje crítico: Sistema reiniciando');
  messageQueue.addLast('Mensaje estándar: Update disponible');

  print('--- Procesando cola de mensajes ---');
  while (messageQueue.isNotEmpty) {
    // removeFirst() en un Queue es O(1), muy eficiente.
    print('Ejecutando: ${messageQueue.removeFirst()}');
  }

  // 2. Uso de HashMap para un caché de sesiones de usuario
  // No necesitamos que las sesiones estén ordenadas, queremos el lookup más rápido.
  final sessionCache = HashMap<String, DateTime>();
  sessionCache['user_abc_123'] = DateTime.now().subtract(Duration(minutes: 5));
  sessionCache['user_xyz_789'] = DateTime.now().subtract(Duration(minutes: 20));
  sessionCache['user_def_456'] = DateTime.now().subtract(Duration(hours: 1));

  print('\n--- Cache de sesiones (Orden no garantizado) ---');
  for (var entry in sessionCache.entries) {
    print('Usuario: ${entry.key} | Última actividad: ${entry.value}');
  }

  // 3. Uso de SplayTreeMap para gestionar niveles de prioridad de alertas
  // Mantiene las claves ordenadas automáticamente mediante un árbol auto-balanceado.
  final alertRegistry = SplayTreeMap<int, String>();
  alertRegistry[3] = 'Baja';
  alertRegistry[1] = 'Crítica';
  alertRegistry[5] = 'Información';
  alertRegistry[2] = 'Alta';

  print('\n--- Registro de alertas (Ordenado por prioridad) ---');
  // La iteración aquí siempre será por orden ascendente de la clave.
  alertRegistry.forEach((prioridad, etiqueta) {
    print('Prioridad $prioridad: $etiqueta');
  });
}

En el ejemplo anterior, la variable messageQueue utiliza la interfaz Queue implementada internamente como ListQueue. A diferencia de un List convencional, ListQueue no necesita desplazar todos los elementos en memoria cuando eliminas el primer elemento; simplemente mueve un puntero en su buffer circular, lo que permite que removeFirst() sea extremadamente rápido independientemente del tamaño de la cola.

Para el caché de sesiones, hemos elegido HashMap en lugar del Map por defecto. Al no necesitar mantener el orden en que se crearon las sesiones, el motor de Dart puede optimizar la distribución de los elementos en la tabla hash, permitiendo que la búsqueda de sessionCache['user_abc_123'] sea una operación de tiempo constante $O(1)$ en promedio.

Finalmente, alertRegistry utiliza SplayTreeMap. A diferencia de un mapa normal, esta estructura mantiene las claves ordenadas en un árbol. Si intentas insertar una nueva prioridad, el árbol se rebalancea automáticamente para mantener la eficiencia de búsqueda y el orden. Esto es vital cuando necesitas iterar sobre los elementos y que aparezcan siempre en una secuencia lógica (en este caso, de menor a mayor prioridad), algo que no puedes garantizar con un HashMap.

El error frecuente

Un error común al implementar colas es utilizar una List estándar con el método removeAt(0). Aunque funciona, es una trampa de rendimiento para aplicaciones de producción.

// MAL: Error de rendimiento
final listaLenta = <String>['A', 'B', 'C', 'D'];
listaLenta.removeAt(0); // O(n) - ¡CUIDADO!

En una List, eliminar el primer elemento obliga a la VM a desplazar todos los elementos restantes una posición hacia la izquierda para rellenar el hueco. Si la lista tiene un millón de elementos, esa operación es pesada. En cambio, con Queue (vía ListQueue), la operación es $O(1)$ porque solo se actualiza un índice interno, sin mover los datos reales.

80

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio