if I remember correctly the default hashCode() implementation in java of an object of type Object() is to return the memory address of the object. When we create our own classes, I read that we want to override hashCode() so that when we insert them into a hash related collection like HashMap() it will work properly. But why is a memory address bad?
Sure we will EVENTUALLY run out of memory and you'll have collisions, but the only case where I see this being a problem is where you are dealing with TONS of data and have very little memory, and then it would START to affect performance because hash related collections in java resolve collisions by chaining(a bucket will link to a list of values that resolved to the same hashcode/index).
The default implementation works fine if every object is unique. But if you override equals() then you are implicitly saying that objects with different addresses can be equivalent to each other. In that case you have to also override hashCode().
Think of the String class.
String s1 = new String("foo");
String s2 = new String("foo");
These two strings are equal and so their hash codes must be equal. But they are distinct objects with different addresses.
s1 == s2 // false, different addresses
s1.equals(s2) // true, same contents
Using their addresses as hash codes would be an error. Therefore, String overrides hashCode() to ensure that equal strings have equal hash codes. This helps meet hashCode()'s contract, which is:
If a.equals(b)
is true, then a.hashCode() == b.hashCode()
.
Bottom line: If you override equals(), also override hashCode().
Exact key objects used while putting objects into hashmap may not be available to access the map later on in your program. So you will end up overriding equals method. When you override equals method you make sure that their hashcodes are also equal, otherwise you cannot retrieve the objects from the hashmap.
While you don't use equals
method it's OK to not define hashCode
, but after that contract of hashCode
is to give same result for objects, that equals. You can't be sure it will, so you need to rewrite it yourself
hashCode
Deciding which hashCode() implementation should be used by the data structure is implicitly a part of the hashmap API.
Providing certain API with information that isn't strictly handled by you, takes the control out of your hands.
Using the default hashCode() implementation couples the keys that you wish to manage to the memory address, which isn't handled by you , and you don't have any control on.
Say that one day you will want to use a certain memory optimizer, which moves objects around memory for consistency and better performance.
You will not be able to use your data structure anymore, for it holds the original hashed addresses of your keys.
In a more practical point of view,
In order to retrieve values from the data structure throughout the program, you will have to keep the references to all of the keys that you have previously inserted (by using another data structure).
You will not be able to use keys that are "similar" in terms of object state.
Person steve1 = new Person("Steve","21","engineer");
Person steve2 = new Person("Steve","21","engineer");
map.put(steve1,"great worker");
map.get(steve2);
// returns null because "steve2" is not considered a key like "steve1"
map.get(steve1);
// returns "great worker"