Search code examples
c#unity-game-engineoverridinghashcodegethashcode

GetHashCode() override coliding way to often


I'm using unity, and unity does not have a tuple in it, so I created my own tuple class to work since I needed it for my Dictionary.

Dictionary <Tuple<int,int>, Tile>

Tile class that I created and isn't really relevant to solve this problem(at least I think it wont help).

But the problem is that I'm using both negative and positive integer in my tuples, and when I use my current GetHashCode() with the Tuples, sometimes I get the same HashCode, for example Tuple<-10, 8> and Tuple<-9,-10> both gives -172 when I return the hashcode.

Is there any good GetHashCode that wouldn't get me conflicts? To be honest I'm only using the operator ==, because I need to check if both tuples have the same integers inside of them, if I could get a operator == that only collides when both integer are the same and in the same order, it would solve my problem.

Some other minor problems, I can't get to understand the Equals override, as it is, it is working, but I don't know how well it works, since I kind of changed every single thing until it worked.

public class Tuple<T1, T2>
{
    public T1 First { get; private set; }
    public T2 Second { get; private set; }

    public Tuple(T1 _First, T2 _Second)
    {
        First = _First;
        Second = _Second;
    }
    public override int GetHashCode()
    {
        int hash = 0;

        hash = First.GetHashCode() * 17 + Second.GetHashCode() + First.GetHashCode();

        return hash;
    }
    public static bool operator==(Tuple<T1, T2> obj1, Tuple<T1, T2> obj2)
    {
        if (ReferenceEquals(null, obj2))
            return false;
        return (obj1.GetHashCode() == obj2.GetHashCode());
    }
    public static bool operator!=(Tuple<T1, T2> obj1, Tuple<T1, T2> obj2)
    {
        if (ReferenceEquals(null, obj2))
            return true;
        return !(obj1.GetHashCode() == obj2.GetHashCode());
    }
    public bool Equals(Tuple<T1, T2> other)
    {
        if (other == null)
            return false;

        if (GetHashCode() == other.GetHashCode())
            return true;
        else
            return false;

    }
    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
        {
            return false;
        }
        if (ReferenceEquals(this, obj))
        {
            return true;
        }

        Tuple<T1, T2> other = obj as Tuple<T1, T2>;

        return obj.GetType() == GetType() && Equals(other);
    }
}
public static class Tuple
{
    public static Tuple<T1, T2> New<T1, T2>(T1 first, T2 second)
    {
        var tuple = new Tuple<T1, T2>(first, second);
        return tuple;
    }
}

Solution

  • GetHashCode() isn't supposed to be collision free. You should use it to determine if two things might be the same objects, and then you have to actually do a thorough check to see if they are.

    For example, your == method should be written more like this:

    public static bool operator==(Tuple<T1, T2> obj1, Tuple<T1, T2> obj2)
    {
        if (ReferenceEquals(null, obj2))
            return false;
    
        if (obj1.GetHashCode() != obj2.GetHashCode())
        {
            return false;
        }
        return DefaultComparer<T1>.Equals(obj1.First, obj2.First) && DefaultComparer<T2>.Equals(obj1.Second, obj2.Second);
    }
    

    Also, don't forget to consider the case where obj1 and obj2 are both null.

    If you're implementing your own Tuple, you might consider just stealing Microsoft's from the Reference Source repository, or at least use it as a base for your own.