Зачем использовать GetHashCode() вместо Equals()?

HashSet<T>.Add сначала сравнивает результаты GetHashCode. Если они равны, он вызывает Equals.

Насколько я понимаю, для реализации GetHashCode необходимо что-то сделать с полями объекта. Простой пример реализации можно найти по адресу Каков наилучший алгоритм для переопределенного System.Object.GetHashCode?.

В моем тесте, сравнивающем оба на 1 000 000 пар объектов, заполненных случайными данными, производительность между ними более или менее одинакова. GetHashCode реализовано так же, как в связанном примере, Equals просто вызывает Equals для всех полей. Так зачем же использовать GetHashCode вместо Equals ?


person user247702    schedule 10.06.2011    source источник
comment
Для большого объекта с большим количеством полей только для чтения хэш-код может быть вычислен в конструкторе и сохранен как дополнительное поле в объекте. В этом случае метод Equals будет использовать поле хэш-кода в качестве оптимизации перед сравнением значений других полей. Если хэш-код отличается, мы можем быть уверены, что остальные поля также различны. Это работает независимо от того, используется ли оно в хеш-таблице или просто используется для сравнения на равенство. GetHashCode возвращает предварительно вычисленное поле хеш-кода. Если некоторые поля также являются неизменяемыми объектами, это может расшириться на много уровней и ускорить сравнение.   -  person Frank Hileman    schedule 08.03.2013
comment
+1 за хороший вопрос. Это хороший вариант бесконечных подобных вопросов по теме GetHashCode на SO.   -  person nawfal    schedule 15.12.2013


Ответы (5)


Для некоторых типов тест Equals может быть относительно дорогим. Обычно он должен сравнивать каждое поле класса. Другими словами, требуется линейное время в зависимости от размера класса. Большие классы дороже сравнивать на равенство.

Что произойдет, если вам нужно сравнить один объект с 1000 других? Звонок Equals 1000 раз может дорого обойтись. Вам нужно сделать N * 2000 обращений к полю, если N - размер класса

GetHashCode вместо этого генерирует «в основном уникальное» целое число на основе содержимого класса. Другими словами, доступ к полям класса осуществляется один раз. И как только вы это сделаете, вы сможете сравнить это целое число с 1000 целыми числами, составляющими хеш-коды других объектов.

Даже в таком наивном варианте использования нам теперь нужно только N * 1000 доступов к полям.

Но что, если мы сохраним хэш-код? Когда мы вставляем объект в набор хэшей, его хеш-код вычисляется один раз. Теперь, в любой раз, когда мы хотим выполнить поиск в хэш-наборе, нам просто нужно вычислить один хэш-код (код внешнего объекта), а затем вы просто получите сравнивать простые целые числа. Таким образом, доступ к полю класса N (для нового объекта, чей хэш-код нам нужно вычислить), плюс ряд целочисленных сравнений, которые варьируются в зависимости от алгоритма, но 1) относительно немногочисленны и 2) дешевы.

person jalf    schedule 10.06.2011
comment
Я не думал о возможности хранить хэш. Понятное объяснение. - person user247702; 10.06.2011
comment
@Stijn: на самом деле хэш-таблицы немного сложнее, но да, основная идея состоит в том, чтобы избежать повторного вычисления хеша. - person jalf; 10.06.2011
comment
на самом деле вам нужно сделать доступ к полю N * 2 * 1000, только если все 1001 объект равны; получение хэш-кода, наоборот, наверняка попадет в каждое поле каждого объекта. В конце концов, вы получаете коэффициент 2 по отношению к сравнению полей в худшем случае, теряя время на получение этих хэшей. - person ubik; 19.02.2013
comment
Не забывайте, что если Equals дорого, то его точный эквивалент GetHashCode будет столько же или дороже. Настоящее преимущество начинается, когда вы вычисляете его только один раз. Хороший вопрос +1. - person nawfal; 15.12.2013

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

person Doc Brown    schedule 10.06.2011

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

person Robert Levy    schedule 10.06.2011
comment
отличный ответ :-). простой и лучший - person Brillia; 25.01.2018

GetHashCode() дает вам интегральное значение, которое вы можете использовать для хеш-таблиц. Этот хеш-код — одна из причин, почему хеш-таблицы так эффективны. Однако может быть более одного объекта с одним и тем же хэш-кодом. Вот почему Equals() называется. Если объекты не равны, они могут попасть в одну корзину, если они равны, то она уже есть в хэш-таблице и ее не нужно добавлять.

person Botz3000    schedule 10.06.2011

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

Например, если поместить в один набор все объекты, где GetHashCode возвращает четное число, а в другой набор все объекты, где GetHashCode возвращает нечетное число, то затем дается объект для поиска, вызов GetHashCode позволит мгновенно исключить из рассмотрения всех объектов в одном из наборов. Если бы вместо двух наборов использовалось двадцать, можно было бы исключить все из девятнадцати наборов. Если 256 наборов, можно удалить 255. Во многих случаях, если настроить количество наборов в зависимости от количества имеющихся у него предметов, можно будет исключить все объекты, кроме горстки, без необходимости смотреть на каждый из них. из них.

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

person supercat    schedule 18.12.2013