简体   繁体   中英

C# SetEquals on HashSet<List<float>> is false when expected true

A Python developer doing some C# (.NET 4.6, Visual Studio 2015 Professional) work here. I am trying to check whether two HashSet s are equal.

I have two HashSet<List<float>> which I am trying to compare using

thisList.SetEquals(otherList);

However, this returns false on my data. Using the sample from the MSDN HashSet 's examples does work as expected. However, in the samples they use HashSet<int> whereas I use HashSet<List<float>> .

As I could not found a way to print the HashSet contents into Immediate Window in Visual Studio ( ToString returns "System.Collections.Generic.HashSet1[System.Collections.Generic.List1[System.Single]]" ), I use Json.NET JsonConvert.SerializeObject(thisList); to dump the data into a .json file on disk.

Two files (each for each HashSet contents is:

[[10.0,15.0],[20.0,25.0]] and [[10.0,15.0],[20.0,25.0]]

Inspecting the HashSet s in Visual Studio while debugging looks like this:

-       thisList    Count = 2   System.Collections.Generic.HashSet<System.Collections.Generic.List<float>>
-       [0] Count = 2   System.Collections.Generic.List<float>
        [0] 10  float
        [1] 15  float
+       Raw View        
-       [1] Count = 2   System.Collections.Generic.List<float>
        [0] 20  float
        [1] 25  float
+       Raw View        
+       Raw View        
-       otherList   Count = 2   System.Collections.Generic.HashSet<System.Collections.Generic.List<float>>
-       [0] Count = 2   System.Collections.Generic.List<float>
        [0] 20  float
        [1] 25  float
+       Raw View        
-       [1] Count = 2   System.Collections.Generic.List<float>
        [0] 10  float
        [1] 15  float
+       Raw View        
+       Raw View        

Each HashSet contains two lists (order is not of relevance, since it is a set) and each list has identical values (with the same order). They should be considered equal.

What should I do to make these HashSet s to be considered equal with thisList.SetEquals(otherList); ?

EDIT:

Printing coord.ToString("G17") on each float:

10
15
20
25
20
25
10
15

Because you are using List in your HashSet, it is comparing the two lists as references instead of considering the values in the Lists.

Instead of using a List to represent an X and Y, use a Vector2 or Point class. This is more or less what the struct should look like:

public struct Point
{
    public double X {get; }
    public double Y { get; }

    public Point(double x, double y)
    {
        X = x;
        Y = y;
    }

    public bool Equals(Point other)
    {
        return X.Equals(other.X) && Y.Equals(other.Y);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        return obj is Point && Equals((Point) obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return (X.GetHashCode() * 397) ^ Y.GetHashCode();
        }
    }
}

You are trying to check equal operation of HashSet<List<float>> with another object of HashSet<List<float>> . The question here is that why it is returning false ?

Now, before we talk about HashSet<List<float>> , let's talk about if I check equal (using below code) for List<float> with another object of List<float> , then what would be the output?

    List<float> list = new List<float>() { 10.0f, 15.0f};
    List<float> anotherList = new List<float>() { 10.0f, 15.0f};

    Console.WriteLine(list.Equals(anotherList));

The output of this will be

false

Since here Equals compare the references of the objects (which aren't equal).

Now to solution to your problem

You should provide a EqualityComparer while initializing the HashSet which should check the type T as needed.

    HashSet<List<float>> HashSet1 = new HashSet<List<float>>(new FloatListComparer());
    anotherHashSet1.Add(list);

    HashSet<List<float>> anotherHashSet2 = new HashSet<List<float>>();
    anotherHashSet2.Add(anotherList);

    Console.WriteLine(anotherHashSet1.SetEquals(anotherHashSet2));

The output of above code is

true

The EqualityComparer I've written here is looks like as follows.

public class FloatListComparer : EqualityComparer<List<float>>
{
    public override bool Equals(List<float> list1, List<float> list2)
    {
        return list1.SequenceEqual(list2);
    }

    public override int GetHashCode(List<float> s)
    {
        return base.GetHashCode();
    }
}

Now question here is that why SetEquals is not working

If you check the implementation of SetEquals at here , then you will find that it calls the default comparer of T which works based on checking the reference of objects. By providing the Comparer, SetEquals uses the specified one.

Check the live fiddler here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM