Sure:
public class Test {
private final int m, n;
public Test(int m, int n) {
this.m = m;
this.n = n;
}
public int hashCode() { return n * m; }
public boolean equals(Object ob) {
if (ob.getClass() != Test.class) return false;
Test other = (Test)ob;
return m == other.m;
}
}
con:
Set<Test> set = new HashSet<Test>();
set.put(new Test(3,4));
boolean b = set.contains(new Test(3, 10)); // false
Técnicamente que debe ser cierto porque m == 3 en ambos casos.
En general, un HashMap funciona de la siguiente manera: tiene un número variable de lo que comúnmente se llama "cubos". El número de depósitos puede cambiar con el tiempo (a medida que se agregan y eliminan entradas) pero siempre es una potencia de 2.
Digamos que un dado HashMap
tiene 16 cubos. Cuando llama a put() para agregar una entrada, se calcula el hashCode() de la clave y luego se toma una máscara según el tamaño de las cubetas. Si (bit a bit) y el hashCode() con 15 (0x0F) obtendrá los últimos 4 bits, lo que equivale un número entre 0 y 15 inclusive:
int factor = 4;
int buckets = 1 << (factor-1) - 1; // 16
int mask = buckets - 1; // 15
int code = key.hashCode();
int dest = code & mask; // a number from 0 to 15 inclusive
Ahora si ya existe una entrada en ese cubo se tiene lo que se llama una colisión . Hay varias maneras de solucionar esto, pero el utilizado por HashMap (y es probablemente el más común en general) es agrupando. Todas las entradas con el mismo hashCode enmascarado se ponen en una lista de algún tipo.
Así que para encontrar si una clave es dada en el mapa ya:
- Calcular el código hash de máscaras;
- Encuentra el cucharón apropiado;
- Si está vacío, no se ha encontrado la clave;
- Si no está vacío, recorra todas las entradas en la comprobación del depósito igual a().
Mirar a través de una cubeta es una operación lineal (O (n)) pero está en un pequeño subconjunto. La determinación del cubo de hashcode es esencialmente constante (O (1)). Si los cubos son suficientemente pequeños, el acceso a un HashMap generalmente se describe como "cerca de O (1)".
Puede hacer un par de observaciones sobre esto.
En primer lugar, si tiene un grupo de objetos que devuelven 42 como código hash, un HashMap
seguirá funcionando, pero funcionará como una lista costosa. El acceso será O (n) (ya que todo estará en el mismo cubo, independientemente de la cantidad de cubos). De hecho, me han preguntado esto en una entrevista.
En segundo lugar, volviendo a su punto original, si dos objetos son iguales (es decir, una. equals(b) == b.equals(a) == true
) pero tienen diferentes códigos hash entonces el HashMap
va a buscarles en (probablemente) el cubo equivocado que resulta en un comportamiento impredecible e indefinido.
Esto no es una respuesta, pero tenga en cuenta que el * propósito completo * de hashCode() es proporcionar un número que cualquier objeto igual debería compartir. Si no fuera por esa propiedad, no tendría razón de existir. –