No es un caso en el que usan hashcodes como un acceso directo en las comparaciones de igualdad tiene sentido.
Considere el caso en el que está construyendo una tabla hash o hashset. De hecho, consideremos los hashsets (los hashtables lo extienden manteniendo también un valor, pero eso no es relevante).
Hay varios enfoques diferentes que uno puede tomar, pero en todos ellos tiene un pequeño número de ranuras en los que se pueden colocar los valores hash, y tomamos el enfoque abierto o cerrado (que solo por diversión, algunas personas usar la jerga opuesta para otros); si colisionamos en la misma ranura para dos objetos diferentes, podemos almacenarlos en la misma ranura (pero teniendo una lista vinculada o tal como se almacenan realmente los objetos) o volviendo a explorar para elegir una ranura diferente (hay varios estrategias para esto).
Ahora, con cualquier enfoque, nos alejamos de la complejidad O (1) que queremos con una tabla hash, y hacia una complejidad O (n). El riesgo de esto es inversamente proporcional al número de ranuras disponibles, por lo que después de un cierto tamaño cambiamos el tamaño de la tabla hash (incluso si todo fuera ideal, eventualmente tendríamos que hacer esto si la cantidad de elementos almacenados fuera mayor que la cantidad máquinas tragamonedas).
Volver a insertar los elementos en un cambio de tamaño dependerá obviamente de los códigos hash. Debido a esto, aunque raramente tiene sentido memorizar GetHashCode()
en un objeto (simplemente no se llama con la frecuencia suficiente en la mayoría de los objetos), sin duda tiene sentido memorizarlo dentro de la tabla hash misma (o quizás, para memorizar un resultado, por ejemplo, si rehiciste hash con un hash de Wang/Jenkins para reducir el daño causado por malas implementaciones de GetHashCode()
).
Ahora, cuando vamos a insertar nuestra lógica va a ser algo así como:
- Obtener código hash para el objeto.
- Obtener ranura para el objeto.
- Si la ranura está vacía, coloque el objeto en ella y vuelva.
- Si la ranura contiene el mismo objeto, hemos terminado para un hashset y tenemos la posición para reemplazar el valor de una hashtable. Haz esto y regresa.
- Pruebe la siguiente ranura de acuerdo con la estrategia de colisión, y regrese al ítem 3 (tal vez cambiando el tamaño si hacemos un bucle con demasiada frecuencia).
Entonces, en este caso tenemos que obtener el código hash antes de comparar para la igualdad. También tenemos el código hash para los objetos existentes ya precalculados para permitir el cambio de tamaño. La combinación de estos dos hechos significa que tiene sentido para poner en práctica nuestra comparación para el artículo 4 como:
private bool IsMatch(KeyType newItem, KeyType storedItem, int newHash, int oldHash)
{
return ReferenceEquals(newItem, storedItem) // fast, false negatives, no false positives (only applicable to reference types)
||
(
newHash == oldHash // fast, false positives, no fast negatives
&&
_cmp.Equals(newItem, storedItem) // slow for some types, but always correct result.
);
}
Obviamente, la ventaja de esto depende de la complejidad de _cmp.Equals
. Si nuestro tipo de clave fuera int
, esto sería un desperdicio total. Si nuestro tipo de clave fuera string y estuviéramos usando comparaciones de igualdad normalizadas Unicode (por lo que no puede ni atajar en longitud), entonces el ahorro bien podría valer la pena.
En general, recordar los códigos hash no tiene sentido porque no se usan con la suficiente frecuencia como para ganar un rendimiento, pero almacenarlos en el hashset o hashtable en sí puede tener sentido.
Como desarrollador, te debes a ti mismo para entender completamente lo que los hashes son Usado para y cómo se relacionan con las tablas hash (según lo implementado por Dictionary y HashSet, entre otros). El artículo de wikipedia para hashtable es un buen comienzo: http://en.wikipedia.org/wiki/Hash_table – spender
@spender: eso es exactamente lo que me ha explicado esta pregunta con más detalle de lo que originalmente entendí o podría recordar. – Armbrat
No solo es incorrecta la comprobación de igualdad, el código es extraño. ¿Por qué multiplicas cero por 397? Puedo decirte en este momento, la respuesta va a ser cero, entonces, ¿por qué hacer que la máquina lo calcule? Por qué xor cero con un valor; esa es una operación de identidad. –