Why is my Dictionary saying a key doesn't exist when I just inserted it? Is it to do with my Equals
method or double comparision?
Here is the code:
// Test: I know the dictionary contains nCoord but its saying the key doesn't exist
Dictionary<UTMCoordinate, int> planes = new Dictionary<UTMCoordinate, int>();
UTMCoordinate nCoord = new UTMCoordinate(337394.136407966, 6263820.40182064, 0, 56, UTMCoordinate.Hemisphere.H_SOUTHERN);
planes[nCoord] = 1;
bool exists = planes.ContainsKey(nCoord); // always returns false
My implementation of UTMCoordinate
is below:
public class UTMCoordinate
{
public enum Hemisphere {H_NOTHERN, H_SOUTHERN};
public const double DIF_TOLERANCE = 0.0005;
public double x { get; set; }
public double y { get; set; }
public double elev { get; set; }
public uint UTMZone { get; set; }
public Hemisphere hemisphere { get; set; }
public UTMCoordinate(double x, double y, double elev=double.MinValue, uint utmZone=uint.MinValue, Hemisphere hemisphere=Hemisphere.H_SOUTHERN) {
this.x = x;
this.y = y;
this.elev = elev;
this.UTMZone = utmZone;
this.hemisphere = hemisphere;
}
public override int GetHashCode() {
unchecked // Overflow is fine, just wrap
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + x.GetHashCode();
hash = hash * 23 + y.GetHashCode();
hash = hash * 23 + elev.GetHashCode();
hash = hash * 23 + UTMZone.GetHashCode();
hash = hash * 23 + hemisphere.GetHashCode();
return hash;
}
}
public override bool Equals(object obj)
{
UTMCoordinate other = obj as UTMCoordinate;
if (other == null)
return false;
return double.Equals(x, other.x) && double.Equals(y, other.y) && double.Equals(elev, other.elev) && uint.Equals(UTMZone, other.UTMZone) && double.Equals(hemisphere, other.hemisphere);
}
}
Edit Using Daniel A. Whites advice I've used a different double comparision method. Unfortunately its still not identifying the key:
public override bool Equals(object obj)
{
//return base.Equals (obj);
UTMCoordinate other = obj as UTMCoordinate;
if (other == null)
return false;
//return double.Equals(x, other.x) && double.Equals(y, other.y) && double.Equals(elev, other.elev) && uint.Equals(UTMZone, other.UTMZone) && double.Equals(hemisphere, other.hemisphere);
return Math.Abs (x-other.x) <= DIF_TOLERANCE && Math.Abs (y-other.y) <= DIF_TOLERANCE && Math.Abs (elev-other.elev) <= DIF_TOLERANCE && uint.Equals(UTMZone, other.UTMZone) && hemisphere == other.hemisphere;
}
If I take your code from your question:
Dictionary<UTMCoordinate, int> planes = new Dictionary<UTMCoordinate, int>();
UTMCoordinate nCoord = new UTMCoordinate(337394.136407966, 6263820.40182064, 0, 56, UTMCoordinate.Hemisphere.H_SOUTHERN);
planes[nCoord] = 1;
bool exists = planes.ContainsKey(nCoord);
The value I get for exists
is true
.
However, if I do this:
nCoord.x = 1.0;
exists = planes.ContainsKey(nCoord);
The value for exists
suddenly becomes false
even though the object is still in the dictionary. This is because the value for GetHashCode
has changed. It was -1473667404
, but after the assignment of the property x
it becomes 201352392
.
The dictionary uses the value of GetHashCode
to determine which bucket to put the key in so when the hash code changes the dictionary may try finding the key in the wrong bucket and then reports that it doesn't contain the key.
I suspect in your code that is what is happening.
So you need to change your object so that it is immutable.
public double x { get; private set; }
public double y { get; private set; }
public double elev { get; private set; }
public uint UTMZone { get; private set; }
public Hemisphere hemisphere { get; private set; }
And then do NOT change any of the values outside of the constructor.