public class TestHashRace
{
private int cachedHash = 0;
private readonly object value;
public object Value
{
get { return value; }
}
public TestHashRace(object value)
{
this.value = value;
}
public override int GetHashCode()
{
if (cachedHash == 0) {
cachedHash = value.GetHashCode();
}
return cachedHash;
}
//Equals isn't part of the question, but since comments request it, here we go:
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((TestHashRace) obj);
}
protected bool Equals(TestHashRace other)
{
return Equals(value, other.value);
}
}
Here's simple test class.
Are we guaranteed that GetHashCode
will always return the same value?
And if we are, can someone point to some reference material which gives us this guarantee?
We aren't worried if it calculates hashcode for our value more than once, we just want to be sure that the returned value will always be the same.
Our class has to be immutable, while the cachedHash
field is mutable. Field cannot be volatile for performance reasons (the whole idea of this question and optimization we are questioning here). Value is immutable. And it has to be thread-safe.
We can live with potential hashcode recalculation when it will be 0 for some specifc values. We do not want to use nullable types or add additional fields for memory reasons (less memory used if we store only 1 int), so it has to be one int field to handle hashcode problem.
Normally, a hashCode is calculated every time you call the hashCode() method. That's usually not a problem, since the calculation is usually not very complex. Sometimes, however, a hashCode needs to be cached for performance reasons.
It is not cryptographically secure, because that's not part of its requirements. How the hashCode is implemented is an implementation detail up to the JVM and can be different for different object types (objects can override the hashCode method and provide an own implementation).
The hashCode() method is used to generate the hash values of objects. Using these hash values, these objects are stored in Java collections such as HashMap, HashSet and HashTable.
Understanding How hashCode() Works Simply put, hashCode() returns an integer value, generated by a hashing algorithm. Objects that are equal (according to their equals()) must return the same hash code.
Are we guaranteed that GetHashCode will always return the same value?
No. The guarantee holds only for immutable value
objects with properly implemented GetHashCode
method. Mutable objects may change their hash code when their content has been mutated (which is the reason why mutable objects should not be used as hash keys).
This is true even if TestHashRace
itself is immutable, because you can do this:
var evil = new StringBuilder("hello");
var thr = new TestHashRace(evil);
RunConcurrentCode(thr);
evil.Append(", world!");
If multiple threads in RunConcurrentCode
start the call to thr
's GetHashCode
at the same time, and then complete on different sides of Append
, the number returned from value.GetHashCode
may be different.
[Edit:] Value is immutable
Then the only thing required for the guarantee to hold is that value
's GetHashCode
is properly implemented, i.e. does not use random stuff etc.
Note: Since zero is a legitimate value for hash code, your code may repeatedly call value
's GetHashCode
when the actual code is zero. One approach to fix this would be using nullable cachedHash
:
int? cachedHash;
...
public override int GetHashCode() {
return cachedHash ?? (cachedHash = value.GetHashCode());
}
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