Использование HashSet и Contains для возврата TRUE, если одно или несколько полей находятся в хэше - PullRequest
1 голос
/ 03 марта 2011

Мне интересно, возможно ли использовать HashSet и сделать метод Содержит , чтобы вернуть 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 и никогда метод равно .Следовательно, метод Содержит return FALSE.

Как я могу заставить мой HashSet возвращать TRUE для приведенного выше примера?Должен ли я использовать словарь вместо этого и добавлять каждое поле в словарь?

Ответы [ 3 ]

3 голосов
/ 03 марта 2011

Ваша реализация GetHashCode () не может вернуть одинаковое значение для двух одинаковых объектов.Поскольку вы требуете совпадения, скажем, на WebId.Затем Uri испортил хеш-код.Или наоборот.Вы не можете это исправить, кроме как возвращением 0. Это убьет перфорацию HashSet <>, поиск будет O (n) вместо O (1).

2 голосов
/ 03 марта 2011

Уместно ли в вашей программе использовать лямбу внутри вызова метода Contains?Это самый простой способ достижения желаемого.

if (hash.Contains(p => p.WedId == product2.WebId))
{
    // "Contains" will now return TRUE because the WebId matches
}
1 голос
/ 03 марта 2011

В недавнем проекте у нас была та же проблема, когда реализация класса Equals () представляла собой логические свойства ORing для определения равенства.Для быстрого выполнения Contains () мы создали несколько IEqualityComparer, каждый из которых проверял ОДНО свойство.Он нужен для каждого свойства, которое ORed в проверке равенства.

    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 (), ему требуется больше памяти для каждого свойства, которое ORed в вашей реализации equals.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...