Когда мы делаем GetHashCode () для словаря? - PullRequest
14 голосов
/ 10 сентября 2009

Я использовал Словарь (TKey, TValue) для многих целей. Но я не сталкивался ни с одним сценарием реализации GetHashCode (), который, как я считаю, связан с тем, что мои ключи были первичных типов, таких как int и string. Мне любопытно знать сценарии (примеры из реальной жизни), когда нужно использовать пользовательский объект для ключа и, таким образом, реализовывать методы GetHashCode () Equals () и т. Д.

И требует ли использование пользовательского объекта для ключа реализации этих функций?

Ответы [ 4 ]

13 голосов
/ 11 сентября 2009

Вы должны переопределить Equals и GetHashCode всякий раз, когда значение по умолчанию Object.Equals (тесты на равенство ссылок) будет недостаточным. Это происходит, например, когда тип вашего ключа является пользовательским типом, и вы хотите, чтобы два ключа считались равными, даже если они не совпадают с экземпляром пользовательского типа.

Например, если ваш ключ такой же простой, как

class Point {
    public int X { get; set; }
    public int Y { get; set; }
}

и вы хотите, чтобы два Point s считались равными, если их X s равны, а их Y s равны, тогда вам нужно будет переопределить Equals и GetHashCode.

12 голосов
/ 11 сентября 2009

Просто чтобы прояснить: есть одна важная вещь о Dictionary<TKey, TValue> и GetHashCode(): Словарь использует GetHashCode, чтобы определить, равны ли два ключа, т.е. если <TKey> имеет пользовательский тип, вам следует позаботиться о реализации GetHashCode() внимательно. Как отметил Эндрю Хэйр, это легко, если у вас есть простой тип, который однозначно идентифицирует ваш пользовательский объект. Если у вас есть комбинированный идентификатор, он становится немного сложнее.

В качестве примера рассмотрим комплексное число как TKey. Комплексное число определяется его действительной и мнимой частью. Оба имеют простой тип, например double. Но как бы вы определили, если два комплексных числа равны? Вы реализуете GetHashode() для своего пользовательского сложного типа и объединяете обе идентифицирующие части.

Более подробную информацию о последних вы найдете здесь .

UPDATE

На основании комментария Эргвуна я проверил поведение Dictionary<TKey, TValue>.Add с особым уважением к реализации TKey Equals(object) и GetHashCode(). я Должен признаться, что я был довольно удивлен результатами.

Учитывая два объекта k1 и k2 типа TKey, два произвольных объекта v1 и v2 типа TValue и пустой словарь d типа Dictionary<TKey, TValue>, это что происходит при добавлении v1 с ключом k1 к d first и v2 с ключом k2 second (в зависимости от реализации TKey.Equals(object) и TKey.GetHashCode()):

k1.Equals(k2)   k1.GetHashCode() == k2.GetHashCode()   d.Add(k2, v2)
false           false                                  ok
false           true                                   ok
true            false                                  ok
true            true                                   System.ArgumentException

Вывод: я ошибся, так как изначально думал, что второй случай (где Equals возвращает false, но оба ключевых объекта имеют одинаковый хэш-код) вызовет ArgumentException. Но в третьем случае словарь каким-то образом использует GetHashCode(). В любом случае, хороший совет - два объекта одинакового типа и одинакового типа должны возвращать один и тот же хэш-код, чтобы гарантировать правильную работу экземпляров Dictionary<TKey, TValue>.

2 голосов
/ 10 сентября 2009

Один из примеров - это когда вам нужно создать составной ключ (это ключ, состоящий из более чем одного фрагмента данных). Этот составной ключ был бы пользовательским типом, который должен был бы переопределять эти методы.

Например, предположим, что у вас был кэш записей адресов в памяти, и вы хотели проверить, был ли адрес в кэше, чтобы сэкономить дорогостоящую поездку в базу данных для ее получения. Скажем также, что адреса являются уникальными с точки зрения их полей улица 1 и почтовый индекс . Вы бы реализовали свой кеш примерно так:

class AddressCacheKey
{
    public String StreetOne { get; set; }
    public String ZipCode { get; set; }

    // overrides for Equals and GetHashCode
}

и

static Dictionary<AddressCacheKey,Address> cache;

Поскольку ваш тип AddressCacheKey переопределяет методы Equals и GetHashCode, они будут хорошим кандидатом на ключ в словаре, и вы сможете определить, нужно ли вам совершать путешествие в базу данных получить запись, основанную на более чем одном фрагменте данных.

1 голос
/ 11 сентября 2009

У вас есть два вопроса здесь.

  1. Когда вам нужно реализовать GetHashCode ()
  2. Будете ли вы когда-нибудь использовать объект для словарного ключа.

Давайте начнем с 1. Если вы пишете класс, который может быть использован кем-то другим, вы захотите определить GetHashCode () и Equals (), когда ссылки Equals () недостаточно. Если вы не планируете использовать его в словаре, и это для вашего собственного использования, то я не вижу причин, чтобы пропустить GetHashCode () и т. Д.

Для 2) вы должны использовать объект в любое время, когда вам нужен постоянный поиск времени от объекта к какому-либо другому типу. Поскольку GetHashCode () возвращает числовое значение, а коллекции хранят ссылки, штраф за использование объекта над Int или строкой отсутствует (помните, что строка является объектом).

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