If we work with Maps in java than the keys need to implement equals and hashcode - Right ? Actually Wrong.
The reason we need hashcode is all Map implementations that we know of are hash based implementations. The reason we always implement equals is that once inside a Map's bucket we need equals to identify the key accurately.
But can the Map work without equals ? Yes. If they key does not implement equals, it inherits the equals method from Object. Thus two keys will be equal if and only if they refer to the same location in memory. An Identity Equality.
This is where the IdentityHashMap makes an entrance. The map is a special implementation that only supports Identity based Equality.
Consider the class Age - a simple class with custom equals and hashcode method.
I then replaced the HashMap with an IdentityHashMap.
Where would we use this implementation ?
From the java docs:
The reason we need hashcode is all Map implementations that we know of are hash based implementations. The reason we always implement equals is that once inside a Map's bucket we need equals to identify the key accurately.
But can the Map work without equals ? Yes. If they key does not implement equals, it inherits the equals method from Object. Thus two keys will be equal if and only if they refer to the same location in memory. An Identity Equality.
This is where the IdentityHashMap makes an entrance. The map is a special implementation that only supports Identity based Equality.
Consider the class Age - a simple class with custom equals and hashcode method.
class Age { public final Integer val; public Age(Integer val) { this.val = val; } @Override public int hashCode() { return val.hashCode(); } @Override public boolean equals(Object obj) { return this.val.equals(((Age) obj).val); } }If I were to test the same against a map:
public static void main(String[] args) { Map<Age, String> map = new HashMap<Age, String>(); map.put(new Age(7), "seven"); System.out.println("One Element added: Size is " + map.size()); Age age7 = new Age(7); map.put(age7, "seven Again"); System.out.println("Two Elements added: Size is " + map.size()); map.put(new Age(7), "Seven a Third time"); System.out.println("Three Elements added: Size is " + map.size()); System.out.println("Value recovered For 7 is : [ " + map.get(age7) + " ]"); }The output:
One Element added: Size is 1 Two Elements added: Size is 1 Three Elements added: Size is 1 Value recovered For 7 is : [ Seven a Third time ]As expected, the three keys all returned true for equals. Hence the map held just one entry. The final object added was retained.
I then replaced the HashMap with an IdentityHashMap.
Map<Age, String> map = new IdentityHashMap<Age, String>();The output is now changed:
One Element added: Size is 1 Two Elements added: Size is 2 Three Elements added: Size is 3 Value recovered For 7 is : [ seven Again ]As seen for the IdentityHashMap:
- Every key used was a different object. Three different memory locations. So all went into the map as three separate enteries.
- The size of three indicates that the equals method is not used here.
- The get call did a memory check and therefore found the second entry.
This class implements the Map interface with a hash table,
using reference-equality in place of object-equality when
comparing keys (and values). In other words, in an
IdentityHashMap, two keys k1 and k2 are considered equal
if and only if (k1==k2). (In normal Map implementations
(like HashMap) two keys k1 and k2 are considered equal
if and only if (k1==null ? k2==null : k1.equals(k2)).)
As it indicates, Identity is used over Object equality.Where would we use this implementation ?
From the java docs:
A typical use of this class is topology-preserving object graph transformations, such as serialization or deep-copying. To perform such a transformation, a program must maintain a "node table" that keeps track of all the object references that have already been processed. The node table must not equate distinct objects even if they happen to be equal. Another typical use of this class is to maintain proxy objects. For example, a debugging facility might wish to maintain a proxy object for each object in the program being debugged.
Hi
ReplyDeleteThis is very good post , can you also write a blog for weakhashmap and weak reference strong reference and phantom reference in java with code example which you usually do :)
Thanks in advance great job
Thanks :)
DeleteYa Java references does sound an interesting thing to blog about. Maybe in a few days You will find a couple of posts on the same !