I have been going through sources of Java Collections framework and noticed that AbstractList
and AbstractMap
return the sum of their elements' hash codes as their own hash code value. This makes List and Map implementstions return distinct hash code values depending on the contents of the collection.
The only use for the hashCode()
method I know is even distribution of elements into buckets inside data structures that use hashing e.g. HashSet
and HashMap
. However, the Set
and Map
contracts specify:
The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set.
and:
The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map.
These contracts make objects that may return distinct hash values depending on their state dangerous to use in sets and as keys in maps. That makes me wonder:
hashCode()
method have besides being used in hash collections?Is there a point in returning a variable hash value?
A data structure doesn't know if you change the hashCode of a key or element. Do so will corrupt the data structure. However, if you mutate the object before adding it to a hash collection, and afterwards not change it, this will work.
Would not it be better to return a constant hash value?
Yes, but that constant must be consistent with equals, in which case the only way to return a constant to not alter any of the field used to create the hashCode().
What use can the hashCode() method have besides being used in hash collections
It can be used to identify individual objects for debugging/logging purposes, though this is more useful if you are using the builtin Object.hashCode() or System.identityHashCode(). It's a quick way to check if two objects are the same reference e.g.
ByteBuffer bb = ByteBuffer.allocate(1024);
ByteBuffer bb2 = ByteBuffer.allocate(1024);
useBB(bb);
useBB(bb2);
public static void useBB(ByteBuffer bb) {
System.out.println(Thread.currentThread()+" - Using bb " + System.identityHashCode(bb));
// do something
}
If you see two threads using the same ByteBuffer you might have a problem....
you can generate it in a constructor upon instantiation like its done for String objects.
String's hashCode is generated on demand to reduce the overhead of creating Strings where you don't need the hashCode. Note: having a String with a hashCode of 0 is expensive though rare unless someone attempts to create strings with a hashCode of 0.
Similarly, the hashCode of Object is generated on demand. It is stored in the header on Oracle/OpenJDK with an initial value of 0 for unset, but is set to 1 - 2^31-1 on first use.
This program benchmarks the cost of Object.hashCode()
by resetting the hashCode to 0.
https://github.com/peter-lawrey/Performance-Examples/blob/master/src/main/java/vanilla/java/unsafe/HashCodePerf.java
This program benchmarks setting the Object.HashCode() with a simpler strategy.
https://github.com/peter-lawrey/Performance-Examples/blob/master/src/main/java/vanilla/java/unsafe/FasterHashCodePref.java
I meant that you would need to generate it based on final fields, so hash would not change.
This is not enough of a guarentee for the hashCode not changing e.g.
class A {
final List<String> strings = new ArrayList<>();
public int hashCode() {
return strings.hashCode();
}
}
Your fields need to be both final
and immutable. to ensure the hashCode won't change. However, if you don't change them, they won't change either.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With