Las hash tables son estructuras de datos que permiten almacenar pares clave-valor y acceder a ellos de manera eficiente. La idea detrás de las hash tables es usar una función matemática llamada "función hash" para asignar a cada clave un índice en un arreglo.
El funcionamiento de las hash tables se basa en dos componentes: la función hash y los buckets. La función hash es una función matemática que recibe una clave y la convierte en un índice en el arreglo. Los buckets son celdas en el arreglo donde se almacenan los valores. Cada bucket está asociado a una clave y contiene un valor.
La función hash es importante porque debe asegurarse de que la misma clave siempre produzca el mismo índice, de lo contrario, se perderá la capacidad de acceder a un valor rápidamente. Además, la función hash debe ser lo suficientemente eficiente para procesar un gran número de claves.
Las operaciones más comunes en una Hash Table son la inserción, búsqueda y eliminación de elementos.
Complejidad algorítmica
Si medimos la complejidad de los métodos de la hash table con Big o notation, podremos notar que es de lo más eficiente. Debido a que siempre se mantiene constante.
Algoritmo
Big o notation
Inserción
O(1)
Búsqueda
O(1)
Eliminación
O(1)
Hay distintas formas de crear e implementar una hash table en JavaScript, por lo que a continuación te enseñaré una manera para crear una con todos sus métodos.
classHashTable{constructor(size){// Dentro del constructor se inicializa un array con un tamaño arbitrario// Para asignarlo como el tamaño total de buckets en nuestra hashTablethis.buckets=newArray(size);this.numBuckets=this.buckets.length;}hash(key){// Esta función toma una "key" (puede ser una cadena, número, etc.)// Para poder calcular el índice del bucket donde el valor será almacenadolet total =0;// En este caso el algoritmo para crear un hash es muy sencillo// Para calcular el hash, se suman los valores ASCII de cada caracter de la key // y se toma el resto de la división de esta suma entre el total de buckets.for(let i =0; i < key.length; i++){ total += key.charCodeAt(i);}return total %this.numBuckets;}insert(key, value){// Este método toma una key y un value, y los almacena en la hash table// Primero se calcula el índice usando la función hashlet index =this.hash(key);// si ese bucket no existe, se inicializa como un array vacío.if(!this.buckets[index]){this.buckets[index]=[];}// Luego se agrega un arreglo con la key y el value al bucketthis.buckets[index].push([key, value]);}get(key){// Esta función toma una key y retorna el valor almacenado en la hash table// Primero se calcula el índice usando la función hashlet index =this.hash(key);// si ese bucket no existe, se retorna null.if(!this.buckets[index]){returnnull}// Si el bucket existe, se recorre el array en busca de un arreglo // que tenga la key especificadafor(let i =0; i <this.buckets[index].length; i++){// Si se encuentra ese bucket, se retorna su value correspondiente. if(this.buckets[index][i][0]=== key){returnthis.buckets[index][i][1];}}//Si no se encuentra la key, se retorna null.returnnull;}retrieveAll(){// Esta función retorna un array con todos los valores almacenados// Se recorren todos los buckets y, si existen, se agrega cada value a un arraylet allValues =[];for(let i =0; i <this.numBuckets; i++){if(this.buckets[i]){for(let j =0; j <this.buckets[i].length; j++){ allValues.push(this.buckets[i][j][1]);}}}// Para finalmente retornarlo.return allValues;}}
Los ejemplos de uso de las hash tables son muy variados. Por ejemplo, se pueden usar para implementar cachés, para hacer búsquedas en grandes cantidades de datos, para implementar diccionarios y mucho más.
Algunos usos prácticos de las hash tables del desarrollo web:
.
Caché de datos que se acceden con frecuencia (resultados de consultas de bases de datos, datos de sesión, etc).
Opciones de configuración para el usuario (puedes usar una clave para cada opción y su correspondiente valor para almacenar las opciones).
Manejar eventos con callbacks (ej. almacenar una referencia a una función de callback con una clave única y luego llamar a la función de callback cuando se dispara el evento correspondiente).
En general, para organización de datos de cualquier tipo, como almacenar productos y categorías de un e-commerce (categorías como claves, productos como valores).
Por lo que veo en el código, un índice (hash) puede contener más de un valor. ¿En la práctica no se supone que un hash es único?
en el método get, por ejemplo, cuando busca por key, siempre devolvería el primer valor, ya que todos los elementos dentro tendrían que tener el mismo key, ¿no?
En una tabla hash, cada clave debería tener un hash único y distinto, lo que significa que cada índice en la tabla hash debería contener solo un valor. Sin embargo, en la práctica, es posible que dos claves diferentes produzcan el mismo hash debido a la función hash utilizada, lo que se conoce como colisión de hash. En este caso, es posible que un índice contenga más de un valor, lo que se llama una lista de colisión. Cuando se busca por una clave en la tabla hash y hay una lista de colisión, el método get devolverá el primer valor que se agregó a la lista de colisión. Es importante tener en cuenta la posibilidad de colisiones de hash al diseñar e implementar una tabla hash y manejar adecuadamente las listas de colisión en caso de que ocurran.
si se implementa ese codigo sale error, lo pueden corregir?
¡Hola @hramirezpa!
¿Qué tipo de error te genera? Los métodos los probamos y no obtuvimos algún error, si nos puedes dejar a detalle que es lo que pasa podemos arreglarlo lo más pronto posible
Hola Leo, bueno no se si es error o inconsistencia o la tabla almacena mas de una vez la misma llave, ejemplo:
const test =newHashTable(10)test.insert("uno",1)test.insert("dos",2)test.insert("tres",3)// Envio la misma información:test.insert("uno",1)test.insert("dos",2)console.log(test.retrieveAll())//Resultado:(5)[2,3,2,1,1]
Pense que esta clase permitia almacenar solo una clave-valor, por favor me aclara?
Muchas gracias