En este ejemplo, a continuación, se crea dos objetos que tienen exactamente la misma estructura interna. Ambos llevan nada más que el valor 1 como una variable de instancia. Mi pensamiento es que si tomo el hash del e1
que debe ser el mismo que el hash de e2
y por lo tanto e1.equals(e2)
debe devolver verdadero.
class EqualsChecker {
public static void main(String[] args) {
Elem e1 = new Elem(1);
Elem e2 = new Elem(1);
System.out.println(e1); // EqualsChecker$Elem@6ff3c5b5
System.out.println(e2); // EqualsChecker$Elem@3764951d
System.out.println("e1.equals(e2): " + e1.equals(e2)); // returns false
}
static class Elem {
private int v;
public Elem(int i) {
this.v = i;
}
}
}
¿Por qué equals
falsa regreso aquí? Creo que tengo el caso media en el siguiente dibujo:
Mira los siguientes puntos de https://docs.oracle.com/javase/9/docs/api/java/lang/Object.html#hashCode--
- Cada vez que se invoca en el mismo objeto más de una vez durante una ejecución de una aplicación Java, el método hashCode debe volver consistentemente el mismo número entero, siempre que no la información utilizada en las comparaciones Igual en se modifica el objeto. Este número entero no necesita permanecer consistentes de una ejecución de una aplicación a otra ejecución de la misma aplicación.
- Si dos objetos son iguales de acuerdo con el método equals (Object), a continuación, una llamada al método hashCode en cada uno de los dos objetos debe producir el mismo resultado entero.
- No es necesario que si dos objetos son desiguales según el método equals (java.lang.Object), después de llamar al método hashCode en cada uno de los dos objetos debe producir resultados enteros distintos. Sin embargo, el programador debe ser consciente de que la producción de resultados enteros distintos de objetos desiguales puede mejorar el rendimiento de las tablas hash.
- Tanto como sea razonablemente práctico, el método hashCode definido por la clase Object no devolver enteros diferentes para objetos distintos. (El hashCode puede o no puede ser implementado como una función de dirección de memoria de un objeto en algún punto en el tiempo).
Ahora, mira el siguiente código y su salida:
class MyEmployee {
String code;
String name;
int age;
public MyEmployee(String code, String name, int age) {
super();
this.code = code;
this.name = name;
this.age = age;
}
}
public class Main {
public static void main(String[] args) {
MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee3 = employee1;
System.out.println(employee1.equals(employee3));
System.out.println("employee1.hashCode(): " + employee1.hashCode());
System.out.println("employee3.hashCode(): " + employee3.hashCode());
System.out.println(employee1.equals(employee2));
System.out.println("employee2.hashCode(): " + employee2.hashCode());
}
}
Salida:
true
employee1.hashCode(): 511833308
employee3.hashCode(): 511833308
false
employee2.hashCode(): 1297685781
Dado que employee3
se apuntan al mismo objeto como employee1
, que está recibiendo el mismo código hash para ellos mientras employee2
está apuntando a un objeto diferente (aunque tiene el mismo contenido, la palabra clave, new
creará un objeto separado en la memoria) y, por tanto, es posible que raramente obtener el mismo código hash para employee2
que el punto # 4 mencionado anteriormente a partir de los estados de documentación:As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects.
Debe invalidar hashCode
el método de manera que debe devolver el mismo código hash para dos objetos tienen el mismo contenido, por ejemplo
class MyEmployee {
String code;
String name;
int age;
public MyEmployee(String code, String name, int age) {
super();
this.code = code;
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((code == null) ? 0 : code.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
}
public class Main {
public static void main(String[] args) {
MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee3 = employee1;
System.out.println(employee1.equals(employee3));
System.out.println("employee1.hashCode(): " + employee1.hashCode());
System.out.println("employee3.hashCode(): " + employee3.hashCode());
System.out.println(employee1.equals(employee2));
System.out.println("employee2.hashCode(): " + employee2.hashCode());
}
}
Salida:
true
employee1.hashCode(): 128107556
employee3.hashCode(): 128107556
false
employee2.hashCode(): 128107556
La implementación de hashCode
dados anteriormente produce el mismo código hash para employee1
y employee2
a pesar de que equals
los rendimientos false
(cheque como el punto # 3 mencionado anteriormente de la documentación).
A manera incorrecta de anular de hashCode
puede resultar incluso en los mismos objetos que regresan diferentes hashcodes por ejemplo,
class MyEmployee {
String code;
String name;
int age;
public MyEmployee(String code, String name, int age) {
super();
this.code = code;
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((code == null) ? 0 : (int) (code.length() * (Math.random() * 100)));
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
}
public class Main {
public static void main(String[] args) {
MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee3 = employee1;
System.out.println(employee1.equals(employee3));
System.out.println("employee1.hashCode(): " + employee1.hashCode());
System.out.println("employee1.hashCode() again: " + employee1.hashCode());
System.out.println("employee3.hashCode(): " + employee3.hashCode());
System.out.println(employee1.equals(employee2));
System.out.println("employee2.hashCode(): " + employee2.hashCode());
}
}
Salida:
true
employee1.hashCode(): 66066760
employee1.hashCode() again: 66069457
employee3.hashCode(): 66073797
false
employee2.hashCode(): 66074882
Esta es la manera incorrecta de anular hashCode
debido a invocar hashCode
en el mismo objeto más de una vez durante una ejecución de una aplicación Java tiene que volver constantemente el mismo entero (cheque como el punto # 1 mencionado anteriormente a partir de la documentación).
Ahora, mira el siguiente código y su salida:
class MyEmployee {
String code;
String name;
int age;
public MyEmployee(String code, String name, int age) {
super();
this.code = code;
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyEmployee other = (MyEmployee) obj;
if (code == null) {
if (other.code != null)
return false;
} else if (!code.equals(other.code))
return false;
return true;
}
}
public class Main {
public static void main(String[] args) {
MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee3 = employee1;
System.out.println(employee1.equals(employee3));
System.out.println("employee1.hashCode(): " + employee1.hashCode());
System.out.println("employee3.hashCode(): " + employee3.hashCode());
System.out.println(employee1.equals(employee2));
System.out.println("employee2.hashCode(): " + employee2.hashCode());
}
}
Salida:
true
employee1.hashCode(): 511833308
employee3.hashCode(): 511833308
true
employee2.hashCode(): 1297685781
Desde employee1.equals(employee2)
rendimientos true
, el código hash también debe ser devuelto mismo (comprobación de punto # 2 se ha mencionado anteriormente de la documentación). Sin embargo, los valores hashcode de employee1
y employee2
son diferentes que no es correcto. Esta diferencia se debe a que no hemos anulado el hashCode
método. Por lo tanto, cada vez que se reemplaza equals
, también se debe anular hashCode
de forma correcta.
Por último, se da a continuación es una forma correcta de poner en práctica hashCode
y equals
:
class MyEmployee {
String code;
String name;
int age;
public MyEmployee(String code, String name, int age) {
super();
this.code = code;
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((code == null) ? 0 : code.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyEmployee other = (MyEmployee) obj;
if (age != other.age)
return false;
if (code == null) {
if (other.code != null)
return false;
} else if (!code.equals(other.code))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
public class Main {
public static void main(String[] args) {
MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee3 = employee1;
System.out.println(employee1.equals(employee3));
System.out.println("employee1.hashCode(): " + employee1.hashCode());
System.out.println("employee3.hashCode(): " + employee3.hashCode());
System.out.println(employee1.equals(employee2));
System.out.println("employee2.hashCode(): " + employee2.hashCode());
}
}
Salida:
true
employee1.hashCode(): 128107556
employee3.hashCode(): 128107556
true
employee2.hashCode(): 128107556