Какова роль GetHashCode в IEqualityComparer in.NET?

Я пытаюсь понять роль метода GetHashCode интерфейса IEqualityComparer.

следующий пример взят из MSDN:

using System;
using System.Collections.Generic;
class Example {
    static void Main() {
        try {

            BoxEqualityComparer boxEqC = new BoxEqualityComparer();

            Dictionary<Box, String> boxes = new Dictionary<Box,
                                                string>(boxEqC);

            Box redBox = new Box(4, 3, 4);
            Box blueBox = new Box(4, 3, 4);

            boxes.Add(redBox, "red");
            boxes.Add(blueBox, "blue");

            Console.WriteLine(redBox.GetHashCode());
            Console.WriteLine(blueBox.GetHashCode());
        }
        catch (ArgumentException argEx) {

            Console.WriteLine(argEx.Message);
        }
    }
}

public class Box {
    public Box(int h, int l, int w) {
        this.Height = h;
        this.Length = l;
        this.Width = w;
    }
    public int Height { get; set; }
    public int Length { get; set; }
    public int Width { get; set; }
}

class BoxEqualityComparer : IEqualityComparer<Box> {

    public bool Equals(Box b1, Box b2) {
        if (b1.Height == b2.Height & b1.Length == b2.Length
                            & b1.Width == b2.Width) {
            return true;
        }
        else {
            return false;
        }
    }

    public int GetHashCode(Box bx) {
        int hCode = bx.Height ^ bx.Length ^ bx.Width;
        return hCode.GetHashCode();
    }
}

разве реализации метода Equals недостаточно для сравнения двух объектов Box? Именно здесь мы сообщаем фреймворку правило, используемое для сравнения объектов. Зачем нужен GetHashCode?

спасибо.

Лукиан

3 ответов


сначала немного фона...

каждый объект в .NET имеет метод Equals и метод GetHashCode.

метод Equals используется для сравнения одного объекта с другим объектом - чтобы увидеть, эквивалентны ли два объекта.

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

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

когда вы хотите получить доступ к значению, вы передаете ключ снова. Метод GetHashCode вызывается на ключе,и ведро, содержащее значение, находится.

когда IEqualityComparer передается в конструктор словаря, IEqualityComparer.Равно и IEqualityComparer.Методы GetHashCode используются вместо методов для ключевых объектов.

теперь, чтобы объяснить, почему необходимы оба метода, рассмотрим это пример:

BoxEqualityComparer boxEqC = new BoxEqualityComparer(); 

Dictionary<Box, String> boxes = new Dictionary<Box, string>(boxEqC); 

Box redBox = new Box(100, 100, 25);
Box blueBox = new Box(1000, 1000, 25);

boxes.Add(redBox, "red"); 
boxes.Add(blueBox, "blue"); 

использование BoxEqualityComparer.Метод GetHashCode в вашем примере оба этих поля имеют один и тот же хэш-код - 100^100^25 = 1000^1000^25 = 25 - хотя это явно не один и тот же объект. Причина, по которой они являются одним и тем же хэш-кодом в этом случае, заключается в том, что вы используете оператор ^ (побитовый exclusive-OR), поэтому 100^100 отменяет оставление нуля, как и 1000^1000. Когда два разных объекта имеют один и тот же ключ, мы называем это столкновением.

когда мы добавляем две пары ключ / значение с тем же хэш-кодом в словарь, они оба хранятся в одном ведре. Поэтому, когда мы хотим получить значение, метод GetHashCode вызывается на нашем ключе, чтобы найти ведро. Поскольку в ведре существует несколько значений, словарь выполняет итерацию по всем парам ключ / значение в ведре, вызывая метод Equals на ключах, чтобы найти правильный.

в Примере, который вы разместили, два поля эквивалентны, поэтому метод Equals возвращает true. В этом случае словарь имеет два одинаковых ключа, поэтому он выдает исключение.

TLDR

таким образом, в целом, метод GetHashCode используется для создания адреса, где хранится объект. Поэтому словарь не должен искать его. Он просто вычисляет хэш-код и прыгает в это место. Метод Equals является лучшим тестом равенства, но не может использоваться для отображения объекта в адресное пространство.

Надежда это помогает


GetHashCode используется в словаре colections и создает хэш для хранения объектов в нем. Вот хорошая статья, почему и как использовать IEqualtyComparer и GetHashCode http://dotnetperls.com/iequalitycomparer


в то время как это было бы возможно для Dictionary<TKey,TValue> его GetValue и подобные методы называют Equals на каждом сохраненном ключе, чтобы увидеть, соответствует ли он искомому, это было бы очень медленно. Вместо этого, как и многие коллекции на основе хэша, он полагается на GetHashCode чтобы быстро исключить большинство несоответствующих значений из рассмотрения. Если вызов GetHashCode на предмет поиска дает 42, а коллекция имеет 53,917 элементов, но вызывает GetHashCode на 53,914 элементов дало другие чем 42, то только 3 пункта придется сравнивать с теми, которые ищут. Остальные 53 914 можно смело игнорировать.

причина a GetHashCode входит в IEqualityComparer<T> должен учитывать возможность того, что потребитель словаря может захотеть рассматривать как равные объекты, которые обычно не считайте друг друга равными. Наиболее распространенным примером может быть вызывающий объект, который хочет использовать строки в качестве ключей, но использовать сравнения без учета регистра. Чтобы это сработало эффективно, словарь должен будет иметь некоторую форму хэш-функции, которая даст то же значение для "Fox" и "FOX", но, надеюсь, даст что-то еще для "box" или "zebra". С GetHashCode метод построен в String не работает таким образом, словарь должен будет получить такой метод откуда-то еще, и IEqualityComparer<T> является наиболее логичным местом, так как необходимость такого хэш-кода будет очень сильно связана с Equals метод, который считает" Fox "и" FOX " идентичными друг другу, но не " боксу "или"зебре".