Я портирую что-то от Java до C#. В Java hashcode
из a ArrayList
зависит от объектов в нем. В C# я всегда получаю тот же хэш-код от a List
...
Почему это?
Для некоторых моих объектов хэш-код должен отличаться, потому что объекты в их свойстве списка делают объекты неравными. Я ожидал бы, что хэш-код всегда уникален для состояния объекта и только равняется другому хэш-коду, когда объект равен.Я неправ?
Для корректной работы хэш-коды должны быть неизменяемыми - хэш-код объекта не должен никогда меняться.
Если хэш-код объекта изменится, все словари, содержащие этот объект, перестанут работать.
Поскольку коллекции не являются неизменяемыми, они не могут реализовать GetHashCode
.
Вместо этого они наследуют стандартный GetHashCode
, который возвращает (надеюсь) уникальное значение для каждого экземпляра объекта. (Обычно основанное на адресе памяти)
Невозможно, чтобы хэш-код был уникальным для всех вариаций большинства нетривиальных классов. В C# концепция равенства списков не такая, как в Java (см. здесь), поэтому реализация хэш-кода также не такая - она отражает равенство списков C#.
Вы ошибаетесь лишь отчасти. Вы определенно ошибаетесь, когда думаете, что одинаковые хэш-коды означают одинаковые объекты, но одинаковые объекты должны иметь одинаковые хэш-коды, что означает, что если хэш-коды различаются, то же самое происходит и с объектами.
Почему слишком философски. Создайте вспомогательный метод (может быть методом расширения) и рассчитайте хэш-код по своему усмотрению. Могут быть хэш-коды элементов XOR
Да, вы ошибаетесь. Как в Java, так и в C # равенство подразумевает наличие одного и того же хэш-кода, но обратное (не обязательно) верно.
См. GetHashCode для получения дополнительной информации.
Основными причинами являются производительность и человеческая природа - люди склонны думать о хэшах как о чем-то быстром, но обычно для этого требуется хотя бы один обход всех элементов объекта.
Пример: если вы используете строку в качестве ключа в хеш-таблице, каждый запрос имеет сложность O (| s |) - используйте строки в 2 раза длиннее, и это будет стоить вам как минимум в два раза дороже. Представьте, что это было развернутое дерево (просто список списков) - ой: -)
Если бы полное, глубокое вычисление хеша было стандартной операцией для коллекции, огромный процент программистов просто использовал бы его невольно, а затем обвинял бы framework и виртуальная машина за то, что они медленные. Для такой дорогостоящей задачи, как полный обход, крайне важно, чтобы программист осознавал всю сложность. Единственное, что нужно было добиться, - это убедиться, что вы должны написать свой собственный. Это тоже хороший сдерживающий фактор: -)
Еще одна причина - обновление тактики . Вычисление и обновление хэша «на лету» по сравнению с выполнением полного расчета каждый раз требует суждения в зависимости от конкретного случая.
Неизменность - это просто академическая отговорка - люди используют хеши для более быстрого обнаружения изменений (например, файловые хэши), а также используют хеши для сложных структур, которые постоянно меняются. У хеша есть гораздо больше применений, помимо основ 101. Ключевым моментом здесь снова является то, что то, что использовать для хеширования сложного объекта, должно быть решающим в каждом конкретном случае.
Использование адреса объекта (на самом деле дескриптора, поэтому он не меняется после сборки мусора) в качестве хэша на самом деле является случаем, когда значение хеш-функции остается неизменным для произвольного изменяемого объекта :-) Причина, по которой C # делает это, заключается в том, что он дешев и снова подталкивает людей рассчитывать свои собственные.