how hashcode or hashmap works, if we override hashcode which returns always a constant and overridden equals methods return false, how it able to identify the exact object while returning or deleting? time bean forgot about the performance all that stuff, my question how it able to identify exact object, let I explain a bit more , i have a person class with two fields and have overridden hashcode which returns always 1 and overridden equals method which returns false, have created 3 objects, object 1 -- id 10 name AAAA, object 2 -- id 20, name BBB, object 3 -- id 30 ,name CCC, I have added all three of objects to hashSet, after that I removed object 2, here how its identifying exact object (20, BBB)
2 Answers
Well, constant hashcode is valid and "only" a performance problem when using HashMap
/HashSet
or other code that uses it to optimize comparisons/searches.
An equals()
implementation that always returns false
, however, breaks the contract of equals and will lead to problems/surprising behavior with many types of collections.
From the JavaDocs of equals
:
The equals method implements an equivalence relation on non-null object references:
- It is reflexive: for any non-null reference value x, x.equals(x) should return true.
- It is symmetric: for any non-null 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 non-null 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 non-null 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 objects is modified.
- For any non-null reference value x, x.equals(null) should return false.
An return false
implementation breaks the first requirement.
From the JavaDocs of hashcode
:
- It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
As @Mensur Qulami pointed out in the comments, it may still appear to work correctly with HashMap
if your implementation uses reference comparison with ==
to optimize the searches for nodes.
From OpenJDK 12 HashMap.getNode(int hash, Object key):
((k = first.key) == key || (key != null && key.equals(k))))
so this implementation checks reference equality before trying equals()
, but that is not guaranteed.
The JavaDocs for HashMap.get
define this strictly in terms of equals()
More formally, if this map contains a mapping from a key k to a value v such that
(key==null ? k==null : key.equals(k))
, then this method returnsv
; otherwise it returnsnull
.
(this is equivalent if the implementation of equals
fulfills the contract above, so the optimization done by OpenJDK is valid)
It's still able to remove the object, even though equals
returns false, because ==
still returns true (==
compares the actual object reference, and is not affected by the equals
implementation).
HashSet remove ultimatly calls HAshMap, removeNode which contains this comparison to see if the value is found.
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
It compares the hashCode, which is always equal since it's a constant, and then first uses ==
to compare. If the key is not the same object (ie. ==
is false), only then equals
is used to check if they are equal.
If you would create a new object with the same value, you would never be able to use it to remove the value from the set, but when you use the same object you still can because of ==