-2

I have a HashSet<int[]> foo where the int[] represents the coordinates of a point in a plane. The value at position 0 represents the x and the value at position 1 represents the y. I want to override the Equals and GetHashCode methods to be able to remove an element (the point represented as an array of size two) if its internal values are equals to a given one.

Already tried:

public override int GetHashCode(){
    return this.GetHashCode();
}

public override bool Equals(object obj){
    if (obj == null || ! (obj is int[])) 
     return false;

    HashSet<int[]> item = obj as HashSet<int[]>;

    return item == this;
}

In my class Maze.

Thanks in advance.

EDIT

I found a way to do that

class SameHash : EqualityComparer<int[]>
{
    public override bool Equals(int[] i1, int[] i2)
    {
        return i1[0] == i2[0] && i1[1] == i2[1]; 
    }


    public override int GetHashCode(int[] i)
    {
        return base.GetHashCode();
    }
}
Raudel Ravelo
  • 648
  • 2
  • 6
  • 24
  • 1
    What is a coordinate? What have you tried, and how is it failing? – a p Oct 20 '17 at 03:11
  • I edited the post. – Wellington Lucas Oct 20 '17 at 03:14
  • I don't know what you're trying to do. What do you think your override of GetHashCode is doing? – Rupert Morrish Oct 20 '17 at 03:23
  • They are doing nothing, that's why i came to ask. Anymay, i found a way to do that. – Wellington Lucas Oct 20 '17 at 03:25
  • Your [GetHashCode(T)](https://msdn.microsoft.com/en-us/library/ms132131.aspx) implementation is really wrong. You should be returning the hashcode of the parameter instead of the instance itself. If I call A.GetHahscode(B), I'd expect the hashcode of B, not A. – Martheen Oct 20 '17 at 03:31
  • Read up on [this answer](https://stackoverflow.com/a/263416/529282). You'd want GetHashCode to give a reasonably fast with acceptably low chance of collisions. Your current GetHashCode will just collide and force the equality comparer to be called. It would still work correctly, and the performance may not suffer much since we're talking about simple 2 integer, but you don't want to have this habit when dealing with expensive Equal operation. – Martheen Oct 20 '17 at 03:38
  • Possible duplicate of [What is the best algorithm for an overridden System.Object.GetHashCode?](https://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode) – Martheen Oct 20 '17 at 03:40

2 Answers2

2

It may seems like you solved what you asked for, but there is something important that should be pointed out. When you implemented the EqualityComparer<int[]> you coded the GetHashCode(int[] i) as return base.GetHashCode(); which is not correct even when it works. I took the time to provide you with the code below for you to see the results of your implementation and I also gave you a possible solution. Copy this code and run it in a Console Project. Comment your line of code, uncomment the line right below it and run it again. You will see the difference! Summarizing, when you return base.GetHashCode() you are returning the same hash code for every item. This causes collisions inside the hash set for all insertions ending up in a behavior as slow as if you were using a List<int[]> and you were asking if it contains an element before inserting it. That is why you will see that by using the function I provided you and for the range of numbers I'm generating you will be able to insert up to one million times in less than 1 sec. However, using yours, no matter the range, it spends 1 sec in around ten thousand insertions. This happens because for all n insertions there are collisions and the resulting time complexity is O(n^2) when the expected for a HashSet and an even distributed Hash Function is O(n). Check this out:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace hashExample
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int[]> points = new List<int[]>();
            Random random = new Random();
            int toInsert = 20000;
            for (int i = 0; i < toInsert; i++)
            {
                int x = random.Next(1000);
                int y = random.Next(1000);
                points.Add(new int[]{ x,y });
            }
            HashSet<int[]> set = new HashSet<int[]>(new SameHash());
            Stopwatch clock = new Stopwatch();
            clock.Start();
            foreach (var item in points)
            {
                set.Add(item);
            }
            clock.Stop();
            Console.WriteLine("Elements inserted: " + set.Count + "/" + toInsert);
            Console.WriteLine("Time taken: " + clock.ElapsedMilliseconds);
        }

        public class SameHash : EqualityComparer<int[]>
        {
            public override bool Equals(int[] p1, int[] p2)
            {
                return p1[0] == p2[0] && p1[1] == p2[1];
            }
            public override int GetHashCode(int[] i)
            {
                return base.GetHashCode();
                //return i[0] * 10000 + i[1];
                //Notice that this is a very basic implementation of a HashCode function
            }
        }    
    }
}
Raudel Ravelo
  • 648
  • 2
  • 6
  • 24
  • Try to avoid thanks comments, the moderator will delete it anyway. Do not get me wrong, It is very welcome, off course. Instead, you can accept or up vote the answer ;) Check this out: https://stackoverflow.com/help/someone-answers – Raudel Ravelo Oct 22 '17 at 03:16
1

The only way I found it possible was by creating a class MyPair instead of using an array (int[]) like you did. Notice that I used X*10000 + Y in the GetHashCode() function but you can change the constant value in order to get a better HashCode for every item or you can create you own. I just provided this one as a simple example and because is an easy way of having different hashCodes when the bounds for X and Y are relative small (less than the root of Int.MaxValue). Here you have the working code:

using System;
using System.Collections.Generic;
using System.Linq;

namespace hash
{

    public class MyPair
    {
        public int X { get; set; }
        public int Y { get; set; }

        public override int GetHashCode()
        {
            return X * 10000 + Y;
        }

        public override bool Equals(object obj)
        {
            MyPair other = obj as MyPair;
            return X == other.X && Y == other.Y;
        }
    }

    class Program
    {

        static void Main(string[] args)
        {
            HashSet<MyPair> hash = new HashSet<MyPair>();
            MyPair one = new MyPair { X = 10, Y = 2 };
            MyPair two = new MyPair { X = 1, Y = 24 };
            MyPair three = new MyPair { X = 111, Y = 266 };
            MyPair copyOfOne = new MyPair { X = 10, Y = 2 };
            Console.WriteLine(hash.Add(one));
            Console.WriteLine(hash.Add(two));
            Console.WriteLine(hash.Add(three));
            Console.WriteLine(hash.Add(copyOfOne));
        }  

    }
}
gviot
  • 58
  • 7
  • Yes, I found many ways to do this with classes, but a hash of type of array of int I did not find anything. I ended up using [Construtor HashSet(IEnumerable, IEqualityComparer) ](https://msdn.microsoft.com/pt-br/library/bb503873(v=vs.110).aspx) to solve the problem. But thanks anyway. – Wellington Lucas Oct 20 '17 at 03:42