Подтвердить что ты не робот

Когда мы делаем GetHashCode() для Словаря?

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

Ниже вы найдете более подробное описание здесь.

UPDATE

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

Учитывая два объекта k1 и k2 типа TKey, два произвольных объекта v1 и v2 типа TValue и пустой словарь d типа Dictionary<TKey, TValue>, это что происходит при добавлении v1 с ключом k1 в d первым и v2 с ключом k2 второй (в зависимости от реализации 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> работают правильно.

Ответ 3

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

Например, скажем, что у вас был кеш-память в памяти в адресных записях, и вы хотите проверить, был ли адрес в кеше, чтобы сохранить дорогостоящую поездку в базу данных для ее получения. Пусть также говорят, что адреса уникальны с точки зрения их полей 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, они будут хорошим кандидатом на ключ в словаре, и вы сможете определить, нужно ли вам совершать поездку в базу данных для получения записи, основанной на более чем одной части данных.

Ответ 4

Здесь у вас есть два вопроса.

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

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

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