Reputation: 3381
In this below example, I create two objects which have exactly the same internal structure. Both carry nothing but the value 1 as an instance variable. My thinking is that if I take the hash of e1
it should be the same as the hash of e2
and therefore e1.equals(e2)
should return true.
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;
}
}
}
Why does equals
return false here? I think I have the middle case in the below sketch:
Upvotes: 1
Views: 203
Reputation: 1613
You need to override the equals
method, otherwise the Object
's equals
method will be used to compare the two instances.
@Override
public boolean equals(Object that) {
if (this == that) return true;
if (that instanceof Elem) {
Elem thatElem = (Elem) that;
return thatElem.v == this.v;
}
return false;
}
Upvotes: 0
Reputation: 78955
Look at the following points from https://docs.oracle.com/javase/9/docs/api/java/lang/Object.html#hashCode--
Now, look at the following code and its output:
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());
}
}
Output:
true
employee1.hashCode(): 511833308
employee3.hashCode(): 511833308
false
employee2.hashCode(): 1297685781
Since employee3
is pointing to the same object as employee1
, you are getting the same hashcode for them while employee2
is pointing to a different object (although it has the same content, the keyword, new
will create a separate object in the memory) and therefore, you may rarely get the same hashcode for employee2
as point#4 mentioned above from the documentation states: As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects.
You must override hashCode
method in a way which should return the same hashcode for two objects having the same content e.g.
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());
}
}
Output:
true
employee1.hashCode(): 128107556
employee3.hashCode(): 128107556
false
employee2.hashCode(): 128107556
The implementation of hashCode
given above produces the same hashcode for employee1
and employee2
even though equals
returns false
(check as point#3 mentioned above from the documentation).
A wrong way of overriding of hashCode
may result in even the same objects returning different hashcodes e.g.
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());
}
}
Output:
true
employee1.hashCode(): 66066760
employee1.hashCode() again: 66069457
employee3.hashCode(): 66073797
false
employee2.hashCode(): 66074882
This is the wrong way of overriding hashCode
because invoking hashCode
on the same object more than once during an execution of a Java application must consistently return the same integer (check as point#1 mentioned above from the documentation).
Now, look at the following code and its output:
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());
}
}
Output:
true
employee1.hashCode(): 511833308
employee3.hashCode(): 511833308
true
employee2.hashCode(): 1297685781
Since employee1.equals(employee2)
returns true
, the hashcode should also be returned same (check point#2 mentioned above from the documentation). However, the hashcode values of employee1
and employee2
are different which is not correct. This difference is because we haven't overridden the hashCode
method. So, whenever you override equals
, you should also override hashCode
in a correct way.
Finally, given below is a correct way of implementing hashCode
and 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());
}
}
Output:
true
employee1.hashCode(): 128107556
employee3.hashCode(): 128107556
true
employee2.hashCode(): 128107556
Upvotes: 1
Reputation: 310983
equals(Object)
's default implementation checks if the two objects are the same instance (i.e. they are ==
). If you want some different logic, you'll have to implement it yourself. Note that if you do this, you should also implement your own hashCode()
, so that two objects that are equal will also have matching hash codes. E.g.:
class Elem {
private int v;
@Override
public boolean equals(final Object o) {
if (o == null || this.getClass() != o.getClass()) {
return false;
}
Elem elem = (Elem) o;
return this.v == elem.v;
}
@Override
public int hashCode() {
return this.v;
}
}
Upvotes: 2