Object class has following five non final methods.
1. clone()
2. equals(Object obj)
3. finalize()
4. hashCode()
5. toString()
Every Java class has Object as a superclass so by default all above methods are provided in every java class. In this article we will discuss in detail the concepts of equals() and hashCode() method. We will discuss why it is necessary to override these methods and how we can override them.
Default implementation of equals() in Object class
The default version of equals method present in the Object class has the same behavior as the == operator which in turn simply checks if two reference variables are referring the same objects.Consider x and y, two object references, then x.equals(y) comparing the memory addresses of the objects referenced by x and y (i.e. not the data of the objects).
The output is
s1.equals(s2)=false
s1.equals(s1_copy)=true
You can see that s1.equals(s2) returns false even if both s1 and s2 contain the same name 'Suresh'.The reason is that Student class is using the Object class provided equals method which checks the referential equality,here it returns false because s1 and a2 are stored at different memory locations.The s1.equals(s1_copy) returns true because s1 and s1_copy is pointing to the same object ,same memory location.
Why to override equals() method
The Object class provided equals method comparing the memory addresses of the objects being referenced by the two reference variables being used with equals method.The general use of the equals method is to compare the data of the objects not its memory addresses.So in this case you have to override the equals method.
NOTE: One exception is that classes like String,Date and Wrapper classes (Integer,Float etc.) override the equals method it inherited from the Object class and implemented logic to compare data of the objects.
Overriding equals method in Java
One should follow the following conditions while overriding equals method.
The output is
s1.equals(s2)=true ( Remember that in the above example it was false :) )
Let us check that how the Student class overridden equals method is following the above mentioned rules.
1) Reflexive
For any reference value x, x.equals(x) should return true can be achieved using the if condition if(this==obj) return true.
2) Symmetric
For any reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true can be achieved using the if condition if(getClass()!=obj.getClass()) return false.
The getClass method (use above) is one of the methods of the Object class and is inherited by all classes from Object. It returns a reference to the Class class for the class-type of the reference it is called on. There is only one Class class reference for a particular Class - this is known as a singleton. All instances of the same Class share the same Class class reference. If you call getClass on any two references of the same type you'll get the same Class class reference value.
If you use instanceof instead of getClass, you can't always ensure the symmetric property.See one scenario
The class Fruit extends Apple and take objects as Fruit f and Apple a.
f.equals(a) =true
a.equals(f) =false
The symmetric property is failed here.So always use getClass instead of instanceof operator while overriding the equals method.
3) null rule
For any non-null reference value x, x.equals(null) should return false can be achieved by the if condition if(obj==null) return false.
NOTE: Other two remaining rules will be obeyed implicitly if you follow these three rules.
Inheritance and the equals Method
The sub class can reuse the base class overridden equals method while overriding the inherited equals method from base class.One example
The output is
obj1.equals(obj2)=true
Notice the absence of null check,reflexive check and class-type check in the sub class equals implementation.If you do them here your code will be logically correct but you will be duplicating logic and wasting CPU cycles.
You can see that Base class and Sub class are loosely coupled here.That means you can modify the Base class equals method without affecting the Sub class equals method and vice versa.
Please visit the following links to learn about hashCode method
Overriding hashCode method in Java
Why override equals() and hashCode() methods of objects as Map keys in Java
1. clone()
2. equals(Object obj)
3. finalize()
4. hashCode()
5. toString()
Every Java class has Object as a superclass so by default all above methods are provided in every java class. In this article we will discuss in detail the concepts of equals() and hashCode() method. We will discuss why it is necessary to override these methods and how we can override them.
Default implementation of equals() in Object class
The default version of equals method present in the Object class has the same behavior as the == operator which in turn simply checks if two reference variables are referring the same objects.Consider x and y, two object references, then x.equals(y) comparing the memory addresses of the objects referenced by x and y (i.e. not the data of the objects).
See the example given below
public class Main { public static void main(String args[]){ Student s1 = new Student("Suresh"); Student s2 = new Student("Suresh"); Student s1_copy = s1; System.out.println("s1.equals(s2)=" + s1.equals(s2)); System.out.println("s1.equals(s1_copy)=" + s1.equals(s1_copy)); } } class Student { String name; Student(String name) { this.name = name; } }
The output is
s1.equals(s2)=false
s1.equals(s1_copy)=true
You can see that s1.equals(s2) returns false even if both s1 and s2 contain the same name 'Suresh'.The reason is that Student class is using the Object class provided equals method which checks the referential equality,here it returns false because s1 and a2 are stored at different memory locations.The s1.equals(s1_copy) returns true because s1 and s1_copy is pointing to the same object ,same memory location.
Why to override equals() method
The Object class provided equals method comparing the memory addresses of the objects being referenced by the two reference variables being used with equals method.The general use of the equals method is to compare the data of the objects not its memory addresses.So in this case you have to override the equals method.
NOTE: One exception is that classes like String,Date and Wrapper classes (Integer,Float etc.) override the equals method it inherited from the Object class and implemented logic to compare data of the objects.
Overriding equals method in Java
One should follow the following conditions while overriding equals method.
- It is reflexive: for any reference value x, x.equals(x) should return true.
- It is symmetric: for any reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
- It is transitive: for any reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
- It is consistent: for any reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the object is modified.
- For any non-null reference value x, x.equals(null) should return false.
Let us discuss the above rules with one simple example
public class Main { public static void main(String args[]){ Student s1 = new Student("Suresh"); Student s2 = new Student("Suresh"); System.out.println("s1.equals(s2)=" + s1.equals(s2)); } } class Student { String name; Student(String name) { this.name = name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) ----------->Reflexive rule return true; if (obj == null) -----------> null rule return false; if (getClass() != obj.getClass()) ------->symmetric rule return false; Student other = (Student) obj; -------->Casting obj to Student if (name == null) { -------------------->Comparing data of both objects if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
The output is
s1.equals(s2)=true ( Remember that in the above example it was false :) )
Let us check that how the Student class overridden equals method is following the above mentioned rules.
1) Reflexive
For any reference value x, x.equals(x) should return true can be achieved using the if condition if(this==obj) return true.
2) Symmetric
For any reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true can be achieved using the if condition if(getClass()!=obj.getClass()) return false.
The getClass method (use above) is one of the methods of the Object class and is inherited by all classes from Object. It returns a reference to the Class class for the class-type of the reference it is called on. There is only one Class class reference for a particular Class - this is known as a singleton. All instances of the same Class share the same Class class reference. If you call getClass on any two references of the same type you'll get the same Class class reference value.
If you use instanceof instead of getClass, you can't always ensure the symmetric property.See one scenario
The class Fruit extends Apple and take objects as Fruit f and Apple a.
f.equals(a) =true
a.equals(f) =false
The symmetric property is failed here.So always use getClass instead of instanceof operator while overriding the equals method.
3) null rule
For any non-null reference value x, x.equals(null) should return false can be achieved by the if condition if(obj==null) return false.
NOTE: Other two remaining rules will be obeyed implicitly if you follow these three rules.
Inheritance and the equals Method
The sub class can reuse the base class overridden equals method while overriding the inherited equals method from base class.One example
public class Main { public static void main(String args[]){ Sub obj1 = new Sub(10, 20); Sub obj2 = new Sub(10, 20); System.out.println("obj1.equals(obj2)=" + obj1.equals(obj2)); } } class Base { int x; Base(int x) { this.x = x; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + x; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Base other = (Base) obj; if (x != other.x) return false; return true; } } class Sub extends Base { int y; Sub(int x, int y) { super(x); this.y = y; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + y; return result; } @Override public boolean equals(Object obj) { if (!super.equals(obj)) return false; Sub other = (Sub) obj; if (y != other.y) return false; return true; } }
The output is
obj1.equals(obj2)=true
Notice the absence of null check,reflexive check and class-type check in the sub class equals implementation.If you do them here your code will be logically correct but you will be duplicating logic and wasting CPU cycles.
You can see that Base class and Sub class are loosely coupled here.That means you can modify the Base class equals method without affecting the Sub class equals method and vice versa.
Please visit the following links to learn about hashCode method
Overriding hashCode method in Java
Why override equals() and hashCode() methods of objects as Map keys in Java
great post keep it up
ReplyDelete