I have a simple class:
public class TileName { int Zoom, X, Y; public override bool Equals (object obj) { var o = obj as TileName; return (o != null) && (o.Zoom == Zoom) && (o.X == X) && (o.Y == Y); } public override int GetHashCode () { return (Zoom + X + Y).GetHashCode(); } }
I was curious if I would get a better distribution of hash codes if I instead did something like:
public override int GetHashCode () { return Zoom.GetHashCode() + X.GetHashCode() + Y.GetHashCode(); }
This class is going to be used as a Dictionary key, so I do want to make sure there is a decent distribution.
For example, the implementation of the GetHashCode() method provided by the String class returns identical hash codes for identical string values. Therefore, two String objects return the same hash code if they represent the same string value.
The GetHashCode method provides this hash code for algorithms that need quick checks of object equality. Syntax: public virtual int GetHashCode (); Return Value: This method returns a 32-bit signed integer hash code for the current object.
GetHashCode returns a value based on the current instance that is suited for hashing algorithms and data structures such as a hash table. Two objects that are the same type and are equal must return the same hash code to ensure that instances of System. Collections.
Why is it important to override GetHashCode ? It s important to implement both equals and gethashcode, due to collisions, in particular while using dictionaries. if two object returns same hashcode, they are inserted in the dictionary with chaining. While accessing the item equals method is used.
Like described by Jon Skeet in this SO answer, it is best practice to pick some prime numbers and multiply these with the single hash codes, then sum everything up.
public int GetHashCode() { unchecked { int hash = 17; // Maybe nullity checks, if these are objects not primitives! hash = hash * 23 + Zoom.GetHashCode(); hash = hash * 23 + X.GetHashCode(); hash = hash * 23 + Y.GetHashCode(); return hash; } }
The problems with xor
hashes are:
X
is equal to Y
then your hash will be just Zoom, because then X ^ Y = X ^ X = 0
holdsxor
is a symmetric operator, it will produce the exact same hashes for the objects [Zoom = 3, X = 5, Y = 7]
, [Zoom = 3, X = 7, Y = 5]
, [Zoom = 7, X = 5, Y = 3]
etc.These facts make the xor-method more likely to cause collisions.
In addition to Jons post, consider using a unchecked
context, for explicitly ignoring overflows. Because like the MSDN says:
If neither
checked
norunchecked
is used, a constant expression uses the default overflow checking at compile time, which is checked. Otherwise, if the expression is non-constant, the run-time overflow checking depends on other factors such as compiler options and environment configuration.
So while usually overflows will be unchecked, it may be that it fails somewhen in some environment or built with some compiler option. But in this case you want to explicitly not check these overflows.
Update:
By the way: someInt.GetHashCode()
returns someInt
. Like this, it is of course the fastest possible and a perfect hash distribution without a single collision. How else would you map an int to an int-hash? :) So what I wanted to say: Your first approach:
return (Zoom + X + Y).GetHashCode();
and your second one:
return Zoom.GetHashCode() + X.GetHashCode() + Y.GetHashCode();
are exactly the same. You dont even have to call GetHashCode
and both are very likely to have collisions. Maybe even worse than the xor
method, if you very likely have small integer values for all three ints.
Update 2:
As I wrote in the comment to ChaosPandions post: If you just have those three int values, and X
, Y
and Zoom
are relatively small numbers (smaller than 1000 or 10000) this one may be also a good hash generator:
public int GetHashCode() { return (X << 16) ^ (Y << 8) ^ Zoom; }
It just distributes the bits in the hash value (example in big-endian for readability):
00000000 00000000 00000011 00110001 X = 817 00000000 00000000 00011011 11111010 Y = 7162 00000000 00000000 00000010 10010110 Zoom = 662 00000011 00110001 00000000 00000000 X << 16 00000000 00011011 11111010 00000000 Y << 8 00000000 00000000 00000010 10010110 Zoom 00000011 00101010 11111000 10010110 (X << 16) ^ (Y << 8) ^ Zoom
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