Inside Story of Equals and Hashcode methods
Steps to Override equals method in Java:
2) Do null check -- if yes then return false.
3) Do the instanceof check, if instanceof return false than return false from equals in Java , after some research I found that instead of instanceof we can use getClass() method for type identification because instanceof check returns true for subclass also, so its not strictly equals comparison until required by business logic. But instanceof check is fine if your class is immutable and no one is going to sub class it. For example we can replace instanceof check by below code
if((obj == null) || (obj.getClass()!= this.getClass()))
return false;
return false;
4) Type cast the object; note the sequence instanceof check must be prior to casting object.
5) Compare individual attribute starting with numeric attribute because comparing numeric attribute is fast and use shortcircuit operator for combining checks. If first field does not match, don't try to match rest of attribute and return false. It’s also worth to remember doing null check on individual attribute before calling equals() method on them recursively to avoid NullPointerExceptionduring equals check in Java.
Common Errors while overriding equals in Java.
1) Instead of overriding equals() method programmer overloaded it.
2) Second mistake I have seen while overriding equals() method is not doing null check for member variables which ultimately results in NullPointerException in Javaduring equals() invocation.
3) Third common mistake is not overriding hashCode method in Java and only overriding equals() method. You must have to override both equals() and hashCode() method in Java
4) Last common mistake programmer make while overriding equals() in Java is not keeping equals() and compareTo() method consistent which is a non formal requirement in order to obey contract of Set to avoid duplicates.
Writing JUnit tests for equals method in Java
· testReflexive() this method will test reflexive nature of equals() method in Java.
· testSymmeteric() this method will verify symmetric nature of equals() in Java.
· testNull() this method will verify null comparison and will pass if equals method returns false.
· testConsistent() should verify consistent nature of equals method in Java.
· testNotEquals() should verify if two object which are not supposed to equals is actually not equal, having negative test cases in test suite is mandatory.
· testHashCode() will verify that if two objects are equal by equals() method in Java then there hashcode must be same. This is an important test if you are thinking to use this object as key in HashMap or Hashtable
5 Tips on writing equals method in Java
Here are some tips to implement equals and hashCode method in Java, this will help you to do it correctly and with ease:
1) Most of the IDE like NetBeans, Eclipse and IntelliJ IDEA provides support to generate equals() and hashcode() method. In Eclipse do the right click-> source -> generate hashCode() and equals().
2) If your domain class has any unique business key then just comparing that field in equals method would be enough instead of comparing all the fields e.g. in case of our example if "id" is unique for every Person and by just comparing id we can identify whether two Person are equal or not.
3) While overriding hashCode in Java makes sure you use all fields which have been used in equals method in Java.
4) String and Wrapper classes like Integer, Float and Double override equals method but StringBuffer doesn’t override it.
5) Whenever possible try to make your fieldsimmutable by using final variables in Java, equals method based on immutable fields are much secure than on mutable fields.
Overriding hashCode method in Java
1) Take a prime hash e.g. 5, 7, 17 or 31 (prime number as hash, results in distinct hashcode for distinct object)
2) Take another prime as multiplier different than hash is good.
3) Compute hashcode for each member and add them into final hash. Repeat this for all members which participated in equals.
4) Return hash .
To view Example Click Here
https://mail-attachment.googleusercontent.com/attachment/u/0/?ui=2&ik=5dd1b9e1fa&view=att&th=13f7be16e8f581c3&attid=0.1&disp=inline&realattid=f_hid8c1a91&safe=1&zw&saduie=AG9B_P8jtkDRyPbChlt1HBsxB2Gx&sadet=1383031042144&sads=HOVFzPpJfX2snmIGm5gs6SFemas
To view Example Click Here
Inside Story Of Cloning ...
· In Java, when you assign an object to another variable, only the memory address of the object is copied and hence any changes in the original object will be reflected in the new variable.
MainObject obj1 = new MainObject(); obj2 = obj1;
· Now cloning comes into picture, when if you don’t want to copy memory address and expecting to copy content values.
So what is cloning?
· Cloning means creating a copy of the object.
This copy of an object can be achieved through the Copy Contractor.
What is copy-constructor?
· A copy constructor is one that takes object of its own type as a single parameter.
EX:
public class Person {
private Car car;
private String name;
public Person(Car c, String n) {
car = c;
name = n;
}
public Person(Person p)
{
name = p.name;
car = new Car(p.car);
// we assume we have a copy
//constructor for Car
}
public String toString() {
return "This is person has " + car;
}
}
public class Car {
public Car() {}
}
|
public class CopyConstructorMain {
public static void main(String [] args) {
Person person1 = new Person(new Car(), "Ben");
Person person2 = new Person(person1);
System.out.println(person1);
System.out.println(person2);
}
}
Output is:
----------
This is person has Car@4edbb0
This is person has Car@3f75e0
|
· There is a limitation with the copy constructor, like if the variable "car" in Person class is actually an instance of a subclass “Benz” then you will get the wrong answer; what is called a slice.
· To avoid this problem we have cloning concept now.
How do you implement cloning in java?
· Implement “Cloneable” interface. (The class registers with the JVM to get permission for cloning)
· Override “clone()” method with a try-catch block for CloneNotSupportedException.
What is Shallow Copy?
- Shallow copy is a bit-wise copy of an object.
- The class variables and associated values will be copied fine.
- But if we have any other class object and respective references, then only reference memory address will be copied for new cloned object.
- When you copy the "ColoredCircle" using clone() method, the fields x and y are copied perfectly with values but field "color" is copied by reference.
- So any changes you make to the color of the original object will be reflected in the copied object and vice versa.
- To avoid that, we have deep copy.
What is Deep Copy?
- The class variables and associated values will be copied fine.
- And also references and associated objects will be copied.
- So changes done in both the classes are independent.
- Change the Color class to implement "Cloneable" interface and clone() method and call the clone() method of color object inside the clone() method of the ColoredCircle object.
- Look at the changed Color class and ColoredCircle class below.
Shallow Copy
|
Deep Copy
|
public class Color {
private String color;
public Color(String c){
this.color = c;
}
//getters and setters for the fields should go here........
}
public class ColoredCircle implements Cloneable {
private int centerX;
private int centerY;
private Color color;
public ColoredCircle(int x, int y, Color c){
this.centerX = x;
this.centerY = y;
this.color = c;
}
public Object clone() {
try {
return (ColoredCircle)super.clone();
}
catch (CloneNotSupportedException e) {
// This should never happen
}
}
//getters and setters for the fields should go here........
}
public class CloneMain {
public static void main(String [] args) {
Color c = new Color("RED");
ColoredCircle c1 = new ColoredCircle(200,200,c);
ColoredCircle c2 = c1.clone();
}
}
|
public class Color implements Cloneable{
private String color;
public Color(String c){
this.color = c;
}
public Object clone() {
try {
return (Color)super.clone();
}
catch (CloneNotSupportedException e) {
// This should never happen
}
}
//getters and setters for the fields should go here........
}
public class ColoredCircle implements Cloneable {
private int centerX;
private int centerY;
private Color color;
public ColoredCircle(int x, int y, Color c){
this.centerX = x;
this.centerY = y;
this.color = c;
}
public Object clone() {
ColoredCircle coloredCircle = null;
try {
coloredCircle = (ColoredCircle)super.clone();
}
catch (CloneNotSupportedException e) {
// This should never happen
}
coloredCircle.color = (Color) color.clone();
return coloredCircle;
}
//getters and setters for the fields should go here........
}
|
- Deep copy can be achieved through serialization, but only limitation is Transient variables will not be copied properly.
Question: if clone is a protected method then why the sub class has to implement Cloneable interface type to use its super class method?
Answer: clone is a dangerous method. So by implementing Cloneable interface you are telling JVM that "There is a good reason to clone my class, so please allow me".
Note:
· While making use of Object clone() method, it will just returns Object type, so this is our responsibility to Type cast to Our class type.
When to do shallow copy and deep copy?
· It’s very simple that if the object has only primitive fields, then obviously you will go for shallow copy.
· But if the object has references to other objects, then based on the requirement, shallow copy or deep copy should be chosen.
· What I mean here is, if the references are not modified anytime, and then there is no point in going for deep copy. You can just opt shallow copy.
· But if the references are modified often, then you need to go for deep copy. Again there is no hard and fast rule; it all depends on the requirement.
Lazy copy:
· A lazy copy is a combination of both shallow copy and deep copy.
· When initially copying an object, a (fast) shallow copy is used.
· When the program wants to modify the original object, it can determine if the data is shared (by examining the counter) and can do a deep copy at that time if necessary.
· Lazy copy looks to the outside just as a deep copy but takes advantage of the speed of a shallow copy whenever possible.
---------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------
Inside Story Of HashMap....
If anybody asks me to describe “How HashMap works?“, I simply answer: “On principle of Hashing“. As simple as it is. Now before answering it, one must be very sure to know at least basics of Hashing. Right??
What is Hashing
Hashing in its simplest form, is a way to assigning a unique code for any variable/object after applying any formula/algorithm on its properties. A true Hashing function must follow this rule:
Hash function should return the same hash code each and every time, when function is applied on same or equal objects. In other words, two equal objects must produce same hash code consistently.
Note: All objects in java inherit a default implementation of hashCode() function defined in Object class. This function produce hash code by typically converting the internal address of the object into an integer, thus producing different hash codes for all different objects.
A little about Entry class
A map by definition is : “An object that maps keys to values”. Very easy.. right?
So, there must be some mechanism in HashMap to store this key value pair. Answer is YES. HashMap has an inner class Entry, which looks like this:
Surely Entry class has key and value mapping stored as attributes. Key has been marked as final and two more fields are there: next and hash. We will try to understand the need of these fields as we go forward.
What put() method actually does
Before going into put() method’s implementation, it is very important to learn that instances of Entry class are stored in an array. HashMap class defines this variable as:
Lets note down the steps one by one:
- First of all, key object is checked for null. If key is null, value is stored in table[0] position. Because hash code for null is always 0.
- Then on next step, a hash value is calculated using key’s hash code by calling its hashCode() method. This hash value is used to calculate index in array for storing Entry object. JDK designers well assumed that there might be some poorly written hashCode() functions that can return very high or low hash code value. To solve this issue, they introduced anotherhash() function, and passed the object’s hash code to this hash() function to bring hash value in range of array index size.
- Now indexFor(hash, table.length) function is called to calculate exact index position for storing the Entry object.
- Here comes the main part. Now, as we know that two unequal objects can have same hash code value, how two different objects will be stored in same array location [called bucket].
Answer is LinkedList. If you remember, Entry class had an attribute “next”. This attribute always points to next object in chain. This is exactly the behavior of LinkedList.
Answer is LinkedList. If you remember, Entry class had an attribute “next”. This attribute always points to next object in chain. This is exactly the behavior of LinkedList.
So, in case of collision, Entry objects are stored in LinkedList form. When an Entry object needs to be stored in particular index, HashMap checks whether there is already an entry?? If there is no entry already present, Entry object is stored in this location.
If there is already an object sitting on calculated index, its next attribute is checked. If it is null, and current Entry object becomes next node in LinkedList. If next variable is not null, procedure is followed until next is evaluated as null.
What if we add the another value object with same key as entered before. Logically, it should replace the old value. How it is done? Well, after determining the index position of Entry object, while iterating over LinkedList on calculated index, HashMap calls equals method on key object for each Entry object. All these Entry objects in LinkedList will have similar hash code but equals() method will test for true equality. If key.equals(k) will be true then both keys are treated as same key object. This will cause the replacing of value object inside Entry object only.
In this way, HashMap ensure the uniqueness of keys.
How get() methods works internally
Now we have got the idea, how key-value pairs are stored in HashMap. Next big question is : what happens when an object is passed in get method of HashMap? How the value object is determined?
Answer we already should know that the way key uniqueness is determined in put() method , same logic is applied in get() method also. The moment HashMap identify exact match for the key object passed as argument, it simply returns the value object stored in current Entry object.
If no match is found, get() method returns null.
Let have a look at code:
Above code is same as put() method till if (e.hash == hash && ((k = e.key) == key || key.equals(k))), after this simply value object is returned.
Key Notes
1. Data structure to store Entry objects is an array named table of type Entry.
2. A particular index location in array is referred as bucket, because it can hold the first element of a LinkedList of Entry objects.
3. Key object’s hashCode() is required to calculate the index location of Entry object.
4. Key object’s equals() method is used to maintain uniqueness of Keys in map.
5. Value object’s hashCode() and equals() method are not used in HashMap’s get() and put() methods.
6. Hash code for null keys is always zero, and such Entry object is always stored in zero index in Entry[].
No comments :
Post a Comment