Search code examples
c#dictionaryhashsetedges

Use HashSet as Dictionary Key - Compare all elements


I am checking if a total group of edges already contains the connection between 2 points.

I want to use HashSet's that will contain 2 vectors as Dictionary keys. Then I want to be able to call a performant Dictionary.ContainsKey(hashSet). I want the contains/equality check to be dependent on the Vectors in the Set.

Fex. If I add HashSet [V000 V001] to the Dict. I want to get Dictionary.ContainsKey(HashSet [V001 V000]) return true. (HashSet, so the order can vary, just the same Elements)

The Problem seems to be, that the Dictionary.ContainsKey() method does see separately created HashSets as different objects, even though, they contain the same elements.

Dictionary<HashSet<Vector3>, Vector3> d = new Dictionary<HashSet<Vector3>, Vector3>();

HashSet<Vector3> s = new HashSet<Vector3>();
s.Add(Vector3.one);
s.Add(Vector3.zero);

d.Add(s);

HashSet<Vector3> s2 = new HashSet<Vector3>();
s2.Add(Vector3.zero);
s2.Add(Vector3.one);

bool doesContain = d.ContainsKey(s2); // should be true

You also may suggest a better way of doing this 'Contains()' check efficiently.


Solution

  • The HashSet type doesn't do the equality comparison you want out of the box. It only has reference equality.

    To get what you want, you'll need a new type to use as the Dictionary key. The new type will have a HashSet property, and overload Equals() and GetHashCode(), and may as well implement IEquatable at this point as well.

    I'll get you started:

    public class HashKey<T> : IEquatable<HashKey<T>>
    {
        private HashSet<T> _items;
        public HashSet<T> Items
        {
            get {return _items;}
            private set {_items = value;}
        }
    
        public HashKey()
        {
            _items = new HashSet<T>();
        }
        public HashKey(HashSet<T> initialSet)
        {
            _items = initialSet ?? new HashSet();
        }
    
        public override int GetHashCode()
        {
            // I'm leaving this for you to do
        }
    
        public override bool Equals(Object obj)
        {
            if (! (obj is HashKey)) return false;
            return this.GetHashCode().Equals(obj.GetHashCode());
        }
    
        public bool Equals(HashSet<T> obj)
        {
            if (obj is null) return false;
            return this.GetHashCode().Equals(obj.GetHashCode());
        }
    }