Para ampliar la respuesta de David Richerby, el término " función hash " está un poco sobrecargado. A menudo, cuando hablamos de una función hash, pensamos en MD5, SHA-1 o algo así como el .hashCode()
método de Java , que convierte alguna entrada en un solo número. Sin embargo, es muy poco probable que el dominio de este número (es decir, el valor máximo) tenga el mismo tamaño que la tabla hash en la que está tratando de almacenar datos. (MD5 tiene 16 bytes, SHA-1 tiene 20 bytes y .hashCode()
es un int
- 4 bytes).
Entonces, su pregunta es sobre el siguiente paso: una vez que tenemos una función hash que puede asignar entradas arbitrarias a números, ¿cómo los colocamos en una estructura de datos de un tamaño particular? ¡Con otra función, también llamada "función hash"!
Un ejemplo trivial de tal función es el módulo ; puede asignar fácilmente un número de tamaño arbitrario a un índice específico en una matriz con módulo. Esto se introduce en CLRS como "el método de división":
En el método de división para crear funciones hash, asignamos una clave en una de las ranuras al tomar el resto de dividido por . Es decir, la función hash eskmkm
h(k)=k mod .m
...
Cuando usamos el método de división, generalmente evitamos ciertos valores de . Por ejemplo, no debería ser una potencia de 2, ya que si entonces es solo los bits de orden más bajo de .mmm=2ph(k)pk
~ Introducción a los algoritmos, §11.3.1 - CLRS
Por lo tanto, el módulo no es una gran función hash, ya que restringe los tamaños que podemos usar de manera segura para nuestra estructura de datos subyacente. La siguiente sección presenta un "método de multiplicación" un poco más complejo, que también utiliza módulo pero es ventajoso porque "el valor de no es crítico". Sin embargo, funciona mejor con algunos conocimientos previos de "características de los datos que se están procesando", algo que a menudo no conocemos.m
Java HashMap
utiliza una versión modificada del método de división que realiza un paso de preprocesamiento para tener en cuenta las .hashCode()
implementaciones débiles para que pueda utilizar matrices de dos tamaños. Puedes ver exactamente lo que está sucediendo en el .getEntry()
método (los comentarios son míos):
// hash() transforms key.hashCode() to protect against bad hash functions
int hash = (key == null) ? 0 : hash(key.hashCode());
// indexOf() converts the resulting hash to a value between 0 and table.length-1
for (Entry<K,V> e = table[indexFor(hash, table.length)];
...
Java 8 trajo consigo una reescritura de la HashMap
cual es aún más rápida, pero un poco más difícil de leer. Sin embargo, utiliza el mismo principio general para la búsqueda de índice.