如果散列中有一个或多个字段,则使用 HashSet 和 Contains 返回 TRUE

发布于 2024-10-19 22:23:52 字数 1964 浏览 1 评论 0原文

我想知道是否可以使用 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 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
    }
}

这是 Product 类的定义

public class Product
{
    public string WebId
    public string Uri

    public Product(string Id, string uri)
    {
        WebId = Id;
        Uri = uri;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != typeof(Product)) return false;
        return Equals((Product)obj);
    }

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

        if (String.Equals(WebId, obj.WebId) || String.Equals(Uri, obj.Uri)) 
            return true;
        else
            return false;
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = 17;

            hash = hash * 23 + WebId.GetHashCode();
            hash = hash * 23 + Uri.GetHashCode();
            return hash;
        }
    }
}

当我运行程序时,方法 Contains 仅运行 GetHashCode 而从不运行方法 等于。因此,方法Contains返回FALSE。

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

I am wondering if it is possible to use a HashSet and make the method Contains to return true if one of the field is in the hash for a giving object.

This is an example of what I would like

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
    }
}

Here is the definition of the class Product

public class Product
{
    public string WebId
    public string Uri

    public Product(string Id, string uri)
    {
        WebId = Id;
        Uri = uri;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != typeof(Product)) return false;
        return Equals((Product)obj);
    }

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

        if (String.Equals(WebId, obj.WebId) || String.Equals(Uri, obj.Uri)) 
            return true;
        else
            return false;
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = 17;

            hash = hash * 23 + WebId.GetHashCode();
            hash = hash * 23 + Uri.GetHashCode();
            return hash;
        }
    }
}

When I run my program, the method Contains only runs GetHashCode and never the method Equals. Hence, the method Contains return FALSE.

How can I make my HashSet to return TRUE for the example above ? Should I be using a Dictionary instead and add each fields to the dictionary ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

韬韬不绝 2024-10-26 22:23:52

您的 GetHashCode() 实现不能保证为两个相等的对象返回相同的值。因为您只需要匹配 WebId。然后 Uri 会搞乱哈希码。或者反过来。除了返回 0 之外,您无法修复此问题。这将杀死 HashSet<> 。 perf,查找将是 O(n) 而不是 O(1)。

Your GetHashCode() implementation isn't guaranteed to return the same value for two objects that are equal. Since you only require a match on, say, WebId. The Uri then screws up the hash code. Or the other way around. You cannot fix this, other than by returning 0. That's going to kill the HashSet<> perf, lookup will be O(n) instead of O(1).

两相知 2024-10-26 22:23:52

在最近的一个项目中,我们遇到了同样的问题,其中类的 Equals() 实现是对属性进行逻辑 ORing 来确定相等性。为了快速执行 Contains(),我们构建了许多 IEqualityComparer,每个 IEqualityComparer 检查一个属性。对于在相等性检查中进行“或”运算的每个属性,您都需要一个。

    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 创建一个哈希表,并将比较器传递给构造函数。将集合插入到每个哈希表中,然后对于要测试的每个项目,对每个哈希表执行 contains() 并对结果进行“或”操作。例如:

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 运算的属性都需要更多的内存。

In a recent project we had the same problem, where the class's Equals() implementation was logical ORing properties to determine equality. To do a quick Contains() we built a number of IEqualityComparer with each one checking ONE property. You need one for each property that is ORed in your equality check.

    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();
            }
        }
    }

Then, create one hashtable per IEqualityComparer, passing in the comparer to the constructor. insert your collection into each hashtable, then for each item you want to test, do a contains() on each hashtable and OR the result. So For example:

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
}

Obviously this method suffers from using quite a bit more memory than the IEnumerable.Contains() method, needs more memory for every property that is ORed in your equals implementation.

画骨成沙 2024-10-26 22:23:52

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

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

Does it fit in your program design to use a lamba inside the Contains method call? It is the most straightforward way I can think of to achieve what you want.

if (hash.Contains(p => p.WedId == product2.WebId))
{
    // "Contains" will now return TRUE because the WebId matches
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文