C# 如果哈希中有一个或多个字段,则使用HashSet和Contains返回TRUE

C# 如果哈希中有一个或多个字段,则使用HashSet和Contains返回TRUE,c#,hashset,C#,Hashset,我想知道如果给定对象的哈希中有一个字段,是否可以使用哈希集并使方法Contains返回true 这是我想要的一个例子 static void Main(string[] args) { HashSet<Product> hash = new HashSet<Product>(); // Since the Id is the same, both products are considered to be the same even if the URI

我想知道如果给定对象的哈希中有一个字段,是否可以使用哈希集并使方法Contains返回true

这是我想要的一个例子

static void Main(string[] args)
{
    HashSet<Product> hash = new HashSet<Product>();

    // Since the Id is the same, both products are considered to be the same even if the URI is not the same
    // The opposite is also true.  If the URI is the same, both products are considered to be the same even if the Id is not the same
    Product product1 = new Product("123", "www.test.com/123.html");
    Product product2 = new Product("123", "www.test.com/123.html?lang=en");

    hash.Add(product1);

    if (hash.Contains(product2))
    {
        // I want the method "Contains" to return TRUE because one of the field is in the hash
    }
}
当我运行我的程序时,该方法只包含运行GetHashCode的语句,而不包含等于的语句。因此,该方法包含returnfalse


对于上面的示例,如何使HashSet返回TRUE?我应该改为使用字典并将每个字段添加到字典中吗?

在Contains方法调用中使用lamba是否适合您的程序设计?这是我能想到的实现你想要的最直接的方法

if (hash.Contains(p => p.WedId == product2.WebId))
{
    // "Contains" will now return TRUE because the WebId matches
}

在Contains方法调用中使用lamba是否适合您的程序设计?这是我能想到的实现你想要的最直接的方法

if (hash.Contains(p => p.WedId == product2.WebId))
{
    // "Contains" will now return TRUE because the WebId matches
}

GetHashCode实现不能保证为两个相等的对象返回相同的值。因为您只需要在WebId上进行匹配。然后Uri将散列代码搞乱。或者反过来说。除返回0外,无法修复此问题。这将杀死HashSet perf,lookup将打开而不是O1。

您的GetHashCode实现不能保证为两个相等的对象返回相同的值。因为您只需要在WebId上进行匹配。然后Uri将散列代码搞乱。或者反过来说。除返回0外,无法修复此问题。这将杀死HashSet perf,lookup将打开而不是O1。

在最近的一个项目中,我们遇到了相同的问题,其中类的Equals实现是用于确定相等性的逻辑ORing属性。为了快速包含,我们构建了许多IEqualityComparer,每个IEqualityComparer检查一个属性。在相等性检查中,每个属性都需要一个OR

    class WebIdComparer : IEqualityComparer<Product>
    {
        public bool Equals(Product x, Product y)
        {
            return Equals(x.WebId, y.WebId);
        }

        public int GetHashCode(Product obj)
        {
            unchecked
            {
                return obj.WebId.GetHashCode();
            }
        }
    }

    class UriComparer : IEqualityComparer<Product>
    {
        public bool Equals(Product x, Product y)
        {
            return Equals(x.Uri, y.Uri);
        }

        public int GetHashCode(Product obj)
        {
            unchecked
            {
                return obj.Uri.GetHashCode();
            }
        }
    }
然后,为每个IEqualityComparer创建一个哈希表,将比较器传递给构造函数。将集合插入到每个哈希表中,然后对要测试的每个项目对每个哈希表和/或结果执行包含操作。例如:

var uriHashTable = new HashSet<Product>(existingProducts, new UriComparer());
var webIdHashTable = new HashSet<Product>(existingProducts, new WebIdComparer());

foreach (var newProduct in newProducts)
{
    if (uriHashTable.Contains(newProduct) || webIdHashTable.Contains(newProduct))
        //then it is equal to an existing product according to your equals implementation
}

显然,此方法比IEnumerable.Contains方法使用的内存要多得多。对于equals实现中的每个OR属性,该方法都需要更多内存。

在最近的一个项目中,我们遇到了相同的问题,其中类的equals实现使用逻辑OR属性来确定相等性。为了快速包含,我们构建了许多IEqualityComparer,每个IEqualityComparer检查一个属性。在相等性检查中,每个属性都需要一个OR

    class WebIdComparer : IEqualityComparer<Product>
    {
        public bool Equals(Product x, Product y)
        {
            return Equals(x.WebId, y.WebId);
        }

        public int GetHashCode(Product obj)
        {
            unchecked
            {
                return obj.WebId.GetHashCode();
            }
        }
    }

    class UriComparer : IEqualityComparer<Product>
    {
        public bool Equals(Product x, Product y)
        {
            return Equals(x.Uri, y.Uri);
        }

        public int GetHashCode(Product obj)
        {
            unchecked
            {
                return obj.Uri.GetHashCode();
            }
        }
    }
然后,为每个IEqualityComparer创建一个哈希表,将比较器传递给构造函数。将集合插入到每个哈希表中,然后对要测试的每个项目对每个哈希表和/或结果执行包含操作。例如:

var uriHashTable = new HashSet<Product>(existingProducts, new UriComparer());
var webIdHashTable = new HashSet<Product>(existingProducts, new WebIdComparer());

foreach (var newProduct in newProducts)
{
    if (uriHashTable.Contains(newProduct) || webIdHashTable.Contains(newProduct))
        //then it is equal to an existing product according to your equals implementation
}

显然,此方法使用的内存比IEnumerable.Contains方法多得多,因此equals实现中的每个OR属性都需要更多的内存。

但请注意,此方法将启用而不是O1,因为它使用的是Enumerable.Contains而不是HashSet.Contains。不过,类似这样的东西可能是最好的选择,因为似乎没有任何方法可以正确满足O1中OP的要求。我得到编译错误,无法将lambda表达式转换为类型“Product”,因为它不是委托类型,但请注意,这将是开的,而不是O1,因为它使用Enumerable.Contains而不是HashSet.Contains。尽管如此,类似这样的东西可能是最好的选择,因为似乎没有任何方法可以正确满足O1中OP的要求。我得到编译错误,无法将lambda表达式转换为类型“Product”,因为它不是委托类型Equals和/或GetHashCode实现被破坏。如果两个对象相等,则它们必须具有相同的哈希代码。你的平等逻辑毫无意义的原因是。相等是一种传递操作。如果a==b和b==c,那么它也意味着a==c。在您的情况下,这是不正确的,这就是为什么您不能让它与GetHashCode一起工作的原因。例如,考虑三个对象A、B和C:A.ID=1,A.URL=A,B.ID=1,B.URL=B和C.ID=2,C.URL=B。在本例中,使用相等逻辑,以下为true:a==b和b==c,这意味着这也应该为true:a==c,但事实并非如此。Equals和/或GetHashCode实现已中断。如果两个对象相等,则它们必须具有相同的哈希代码。你的平等逻辑毫无意义的原因是。相等是一种传递操作。如果a==b和b==c,那么它也意味着a==c。在您的情况下,这是不正确的,这就是为什么您不能让它与GetHashCode一起工作的原因。例如,考虑三个对象A、B和C:A.ID=1,A.URL=A,B.ID=1,B.URL=B和C.ID=2,C.URL=B。在本例中,使用相等逻辑,以下是正确的:a==b和b==c,这意味着这也应该是b
e true:a==c,但事实并非如此。您的解决方案也会遇到与OP的相等逻辑相同的症状。它是不可传递的,在你的例子中,如果a==b和b==c,你会期望a==c也是真的,但它不会发生。其中一个原因可能是,对象a和b基于第一个条件相等,对象b和c基于第二个条件相等,而对象a和c都不满足这两个条件。您的解决方案还有与OP的相等逻辑相同的症状。它是不可传递的,在你的例子中,如果a==b和b==c,你会期望a==c也是真的,但它不会发生。其中一个原因可能是对象a和b根据第一个条件相等,对象b和c根据第二个条件相等,而对象a和c两个条件都不满足。