Los hashmaps son una estructura de datos poderosa en JavaScript que permite el almacenamiento y recuperación rápida de pares clave-valor. En este artículo, explicaremos el concepto de hashmaps, los compararemos con los objetos de JavaScript y examinaremos su complejidad temporal y uso en JavaScript.
Entendiendo los Hashmaps en JavaScript
¿Qué son los Hashmaps?
Los hashmaps son una estructura de datos que almacena pares clave-valor, donde cada clave es única y se mapea a un valor. La clave se usa para indexar el valor, permitiendo una recuperación rápida del valor asociado. Los hashmaps usan una función hash para calcular un índice para almacenar y recuperar valores. La función hash toma la clave como entrada y genera un código hash único, que luego se usa para determinar el índice donde se almacenará el valor en el array o bucket.
Hashmaps vs Objetos de JavaScript
Los objetos de JavaScript también pueden almacenar pares clave-valor, similar a los hashmaps. Sin embargo, los hashmaps ofrecen ventajas sobre los objetos:
-
Tipos de Clave: Los hashmaps permiten usar cualquier tipo de dato como clave, incluyendo objetos, arrays y funciones. Los objetos de JavaScript están limitados a usar solo strings y símbolos como claves.
const hashmap = new Map(); hashmap.set({ name: 'John' }, 25); hashmap.set([1, 2, 3], 'array'); const obj = {}; obj[{ name: 'John' }] = 25; // Inválido: Los objetos no pueden usarse como claves en objetos -
Mantenimiento del Orden: Los hashmaps mantienen el orden de inserción de los elementos, lo que significa que el orden en el que se agregan los pares clave-valor al hashmap se preserva. Los objetos de JavaScript no garantizan ningún orden de sus propiedades.
const hashmap = new Map(); hashmap.set('a', 1); hashmap.set('b', 2); hashmap.set('c', 3); console.log(Array.from(hashmap.keys())); // Salida: ['a', 'b', 'c'] const obj = { a: 1, b: 2, c: 3 }; console.log(Object.keys(obj)); // Salida: ['a', 'b', 'c'] (No garantizado) -
Seguimiento del Tamaño: Los hashmaps proporcionan métodos como
size()para obtener el número de elementos almacenados en el hashmap. Con los objetos de JavaScript, necesitarías llevar un seguimiento manual del número de propiedades o usarObject.keys(obj).lengthpara determinar el tamaño.const hashmap = new Map(); hashmap.set('a', 1); hashmap.set('b', 2); console.log(hashmap.size); // Salida: 2 const obj = { a: 1, b: 2 }; console.log(Object.keys(obj).length); // Salida: 2
Complejidad Temporal de las Operaciones de Hashmap
Una de las principales ventajas de los hashmaps es su complejidad temporal para las operaciones básicas. Los hashmaps proporcionan una complejidad temporal constante, denotada como O(1), para las siguientes operaciones:
-
Inserción: Agregar un nuevo par clave-valor al hashmap es una operación O(1). La función hash se usa para calcular el índice, y el par clave-valor se almacena en ese índice en el array o bucket.
-
Eliminación: Eliminar un par clave-valor del hashmap también es una operación O(1). La función hash se usa para localizar el índice donde se almacena el par clave-valor, y luego se elimina de ese índice.
-
Búsqueda: Recuperar el valor asociado con una clave dada es una operación O(1). La función hash se usa para calcular el índice basado en la clave, y el valor puede ser accedido en ese índice.
Ejemplo: Caché
Los hashmaps se usan comúnmente para caché. Supongamos que tienes un sitio web que frecuentemente obtiene datos de una API externa. Para mejorar el rendimiento y reducir el número de llamadas a la API, puedes implementar un mecanismo de caché usando un hashmap.
const cache = new Map();
function fetchData(key) {
if (cache.has(key)) {
return cache.get(key); // Recuperar datos del caché (búsqueda O(1))
} else {
const data = fetchFromAPI(key); // Obtener datos de la API
cache.set(key, data); // Almacenar datos en caché (inserción O(1))
return data;
}
}
En este ejemplo, la función fetchData verifica si los datos solicitados ya están presentes en el caché usando el método has(). Si lo están, los datos se recuperan del caché usando el método get(), evitando la necesidad de hacer una llamada a la API. Si los datos no se encuentran en el caché, se obtienen de la API y luego se almacenan en el caché usando el método set() para solicitudes futuras.
Uso del Objeto Map de JavaScript para Crear Hashmaps
JavaScript tiene un objeto Map integrado que te permite crear y usar hashmaps. Aquí está cómo puedes usar el objeto Map para implementar hashmaps en JavaScript:
Creando un Hashmap
Para crear un nuevo hashmap, usa el constructor new Map(). Esto crea un hashmap vacío para almacenar pares clave-valor.
const myHashmap = new Map();
Por ejemplo, para crear un hashmap para almacenar datos de estudiantes:
const studentMap = new Map();
Agregando Pares Clave-Valor
Para agregar un nuevo par clave-valor al hashmap, usa el método set(). Toma dos argumentos: la clave y el valor.
myHashmap.set('name', 'John');
myHashmap.set('age', 25);
En el ejemplo anterior, agregamos dos pares clave-valor al myHashmap: 'name' se mapea a 'John' y 'age' se mapea a 25.
Agreguemos algunos datos de estudiantes a nuestro studentMap:
studentMap.set('1', { name: 'John', age: 20, major: 'Computer Science' });
studentMap.set('2', { name: 'Alice', age: 22, major: 'Mathematics' });
studentMap.set('3', { name: 'Bob', age: 21, major: 'Physics' });
Obteniendo Valores
Para obtener el valor de una clave dada, usa el método get(). Toma la clave como argumento y devuelve el valor.
const name = myHashmap.get('name');
console.log(name); // Salida: 'John'
En este ejemplo, obtenemos el valor para la clave 'name' del myHashmap, que es 'John'.
Para obtener los datos de un estudiante específico de studentMap:
const student = studentMap.get('2');
console.log(student); // Salida: { name: 'Alice', age: 22, major: 'Mathematics' }
Verificando la Existencia de una Clave
Para verificar si una clave existe en el hashmap, usa el método has(). Toma la clave como argumento y devuelve un valor booleano.
const hasAge = myHashmap.has('age');
console.log(hasAge); // Salida: true
Aquí, verificamos si la clave 'age' existe en el myHashmap usando el método has(), que devuelve true.
Verificando si un estudiante con ID '4' existe en studentMap:
const hasStudent = studentMap.has('4');
console.log(hasStudent); // Salida: false
Eliminando Pares Clave-Valor
Para eliminar un par clave-valor del hashmap, usa el método delete(). Toma la clave como argumento y elimina el par clave-valor del hashmap.
myHashmap.delete('age');
console.log(myHashmap.has('age')); // Salida: false
En este ejemplo, eliminamos el par clave-valor con la clave 'age' del myHashmap usando el método delete(). Después de la eliminación, has('age') devuelve false.
Eliminando un estudiante de studentMap:
studentMap.delete('1');
console.log(studentMap.has('1')); // Salida: false
Obteniendo el Tamaño del Hashmap
Para obtener el número de pares clave-valor en el hashmap, usa la propiedad size. Devuelve el tamaño del hashmap.
console.log(myHashmap.size); // Salida: 1
Aquí, accedemos a la propiedad size del myHashmap, que devuelve 1, indicando que hay un par clave-valor en el hashmap.
Obteniendo el número de estudiantes en studentMap:
console.log(studentMap.size); // Salida: 2
Resumen de Operaciones de Hashmap
| Operación | Método/Propiedad | Descripción |
|---|---|---|
| Crear un hashmap | new Map() |
Crea un hashmap vacío |
| Agregar un par clave-valor | set(key, value) |
Agrega un nuevo par clave-valor al hashmap |
| Obtener un valor por clave | get(key) |
Obtiene el valor para la clave dada |
| Verificar existencia de clave | has(key) |
Verifica si una clave existe en el hashmap |
| Eliminar un par clave-valor | delete(key) |
Elimina el par clave-valor con la clave dada |
| Obtener el tamaño del hashmap | size |
Devuelve el número de pares clave-valor en el hashmap |
Estas son las operaciones básicas que puedes realizar en un hashmap usando el objeto Map de JavaScript. El objeto Map proporciona una forma de trabajar con pares clave-valor y ofrece métodos para manipular y acceder a los datos en el hashmap.
El objeto Map mantiene el orden de los pares clave-valor, lo que significa que cuando iteras sobre el hashmap usando métodos como forEach() o el bucle for...of, los pares clave-valor se accederán en el orden en que fueron agregados.
Iterando sobre Hashmaps en JavaScript
Los hashmaps de JavaScript, también conocidos como objetos Map, tienen métodos para iterar sobre sus pares clave-valor. Dos formas de hacerlo son el método forEach() y el bucle for...of. Veamos cada uno de estos en detalle.
El Método forEach()
El método forEach() está integrado en el objeto Map. Te permite iterar sobre cada par clave-valor en el hashmap. Usa una función callback que se ejecuta para cada par.
La función callback recibe tres parámetros:
value: El valor del par actual.key: La clave del par actual.map: El hashmap sobre el que se llamóforEach().
Ejemplo
Aquí hay un ejemplo que usa forEach() para iterar sobre un hashmap y calcular el precio total de artículos en un carrito de compras:
const shoppingCart = new Map();
shoppingCart.set('shirt', 25);
shoppingCart.set('pants', 50);
shoppingCart.set('shoes', 75);
let totalPrice = 0;
shoppingCart.forEach((price) => {
totalPrice += price;
});
console.log(`Total Price: $${totalPrice}`);
Salida:
Total Price: $150
En este ejemplo, tenemos un hashmap shoppingCart con artículos y precios. Usamos forEach() para recorrer cada par y sumar los precios para obtener el total.
El Bucle for...of
También puedes usar un bucle for...of para iterar sobre un hashmap. El bucle for...of funciona con objetos iterables como arrays, strings y maps.
Cuando usas for...of con un hashmap, cada bucle devuelve un array con el par [key, value].
Visualizando la Iteración de Hashmap
Aquí hay un diagrama que muestra cómo funciona iterar sobre un hashmap:
El diagrama muestra el hashmap e iterar sobre cada par clave-valor. Para cada par, lo procesamos según lo que necesitemos hacer, como cálculos, comparaciones u otras operaciones.
Tanto forEach() como for...of te permiten iterar sobre los pares clave-valor de un hashmap en JavaScript. Cuál uses depende de tus necesidades específicas. forEach() es bueno cuando quieres hacer algo para cada par, mientras que for...of es útil cuando necesitas tanto las claves como los valores en cada bucle.
Ten en cuenta que el orden de iteración en un hashmap se basa en el orden en que se agregaron los pares. El objeto Map de JavaScript mantiene este orden.
Iterar sobre hashmaps es común cuando trabajas con pares clave-valor, y JavaScript tiene estos métodos para hacerlo más fácil y rápido. Para más información sobre objetos Map e iteración, consulta los MDN Web Docs.
Manejo de Colisiones en Hashmaps
¿Qué son las Colisiones?
En un hashmap, las colisiones ocurren cuando dos o más claves se hashean al mismo índice. Esto significa que múltiples pares clave-valor necesitan ser almacenados en la misma posición en el array o bucket. Si no se manejan bien, las colisiones pueden llevar a la sobrescritura de datos o lentitud en la recuperación de valores.
Aquí hay un ejemplo de una colisión en un hashmap:
En este ejemplo, tanto las claves 'John' como 'Alice' se hashean al mismo índice (Índice0), causando una colisión.
Si las colisiones no se corrigen, estos problemas pueden ocurrir:
-
Sobrescritura de Datos: Si un nuevo par clave-valor se hashea al mismo índice que un par existente, el par existente podría ser sobrescrito, causando pérdida de datos.
-
Recuperación Lenta: Cuando ocurren colisiones, encontrar valores se vuelve más lento ya que el hashmap necesita hacer más pasos para encontrar el valor correcto entre los pares colisionados.
Ejemplo: Donde pueden ocurrir colisiones
- En una aplicación de directorio telefónico, si dos personas tienen el mismo apellido, sus entradas podrían colisionar en el hashmap.
- En una base de datos de estudiantes, si los IDs de estudiante se usan como claves y dos estudiantes tienen el mismo ID, sus registros colisionarán.
Para solucionar estos problemas, los hashmaps usan técnicas de manejo de colisiones.
Formas de Manejar Colisiones
Hay dos formas principales de manejar colisiones en hashmaps:
1. Encadenamiento Separado
- Cada índice del array o bucket del hashmap almacena una lista enlazada de pares clave-valor.
- Cuando ocurre una colisión, el nuevo par clave-valor se agrega a la lista enlazada en ese índice.
- Encontrar un valor significa recorrer la lista enlazada en el índice hasheado para encontrar el par clave-valor deseado.
2. Direccionamiento Abierto
- El direccionamiento abierto no usa listas enlazadas para manejar colisiones. En cambio, busca el siguiente índice libre en el array cuando ocurre una colisión.
- Hay diferentes formas de encontrar el siguiente índice, como sondeo lineal, sondeo cuadrático y hashing doble.
- Cuando ocurre una colisión, el hashmap usa la forma de sondeo para encontrar el siguiente espacio vacío en el array para almacenar el par clave-valor.
Mejores Prácticas para Usar Hashmaps
- Elige una buena función hash que reduzca colisiones y distribuya claves uniformemente.
- Piensa en el número esperado de elementos y el rendimiento deseado al elegir el tamaño inicial del hashmap.
- Sé consciente de cómo las colisiones pueden afectar la velocidad de las operaciones del hashmap, especialmente con muchas colisiones.
- Usa encadenamiento separado o direccionamiento abierto según lo que tu aplicación necesite, considerando aspectos como el uso de memoria y la velocidad de recuperación.





