C# GetHashCode с двумя Int16 тоже возвращает только до Int32?

Извините, что объединяю два вопроса в один, они взаимосвязаны.

HashCodes вместо HashSets и тому подобное. Насколько я понимаю, они должны быть уникальными, не меняться и представлять любую конфигурацию объекта в виде одного числа.

Мой первый вопрос заключается в том, что для моего объекта, содержащего два Int16 a и b, безопасно ли, чтобы мой GetHashCode возвращал что-то вроде a * n + b, где n - большое число, я думаю, возможно, Math.Pow(2, 16)?

Также GetHashCode, по-видимому, негибко возвращает именно тип Int32.

32 бита могут хранить, например, два Int16, один символ юникода или 16 направлений компаса N, S, E, W, это немного, даже что-то вроде небольшого графа с несколькими узлами, вероятно, было бы слишком много для этого. Представляет ли это ограничение коллекций C# Hash?


person alan2here    schedule 14.04.2012    source источник
comment
Я бы использовал (a << 16) | b, который уникален и не выполняет сложных математических операций.   -  person Marc Gravell    schedule 14.04.2012
comment
Это не уникально, вы забываете о расширении знака. (а ‹‹ 16) | (b&0xffff) уникален.   -  person Hans Passant    schedule 14.04.2012


Ответы (2)


Я так понимаю, они должны быть уникальными

Неа. Они не могут быть уникальными для большинства типов, которые могут иметь более 232 возможных значений. В идеале, если два объекта имеют одинаковый хеш-код, то они маловероятно равны, но вы никогда не должны предполагать, что они равны. Важным моментом является то, что если у них разные хеш-коды, они обязательно должны быть неравными.

Мой первый вопрос заключается в том, что для моего объекта, содержащего два Int16 a и b, безопасно ли, чтобы мой GetHashCode возвращал что-то вроде a * n + b, где n — большое число, я думаю, возможно, Math.Pow (2, 16) .

Если он только содержит два значения Int16, его проще всего использовать:

return (a << 16) | (ushort) b;

Тогда значение будет уникальным. Ура!

Также GetHashCode, по-видимому, негибко возвращает именно тип Int32.

Да. Такие типы, как Dictionary и HashSet, должны иметь возможность использовать фиксированный размер, чтобы они могли работать с ним для помещения значений в сегменты.

32 бита могут хранить, например, два Int16, один символ юникода или 16 направлений компаса N, S, E, W, это немного, даже что-то вроде небольшого графа с несколькими узлами, вероятно, было бы слишком много для этого. Представляет ли это ограничение коллекций C# Hash?

Если бы это было было ограничением, это было бы ограничение .NET, а не ограничение C#, но нет, это просто непонимание того, что хеш-коды должны представлять.

У Эрика Липперта отличный (очевидно) сообщение в блоге о GetHashCode, которое вы должны прочитать для получения дополнительной информации.

person Jon Skeet    schedule 14.04.2012
comment
Спасибо за четкий ответ. Они не должны быть уникальными? Таким образом, он может вернуть 0; а оно бы еще работало, если бы неэффективно? - person alan2here; 14.04.2012
comment
@alan2here: Да. Возврат константы всегда является допустимой стратегией для хеш-кода, но она побеждает всю обычную эффективность, связанную с хеш-таблицами и т. д. - person Jon Skeet; 14.04.2012
comment
, если два объекта имеют одинаковый хэш-код, то они ‹strike›un‹/strike›равны, но вы никогда не должны предполагать, что они ‹strike›un‹/strike›равны Вы немного перепутали это предложение - person CodesInChaos; 14.04.2012
comment
Таким образом, хеш-код — это своего рода численная оценка сходства. Также я получаю оператор Bitwise-or, используемый в операнде с расширенным знаком; рассмотрите приведение к меньшему беззнаковому типу с первым предупреждением при возврате (a ‹‹ 16) | b;, пока игнорирую. - person alan2here; 14.04.2012
comment
@alan2here: Нет, это не оценка сходства - два очень похожих значения могут иметь очень разные хеш-коды. Все, что представляет значение, является потенциальным равенством. И да, вы можете игнорировать предупреждение, которое, как я полагаю, исходит от ReSharper? - person Jon Skeet; 14.04.2012
comment
У меня нет модов Visual Studio. Потенциальное равенство, тыс :¬) - person alan2here; 14.04.2012
comment
Джон, как Ханс Пассант напомнил Марку Гравеллу в комментариях к вопросу, @alan2here не должен игнорировать это предупреждение. Если да, то для любого отрицательного значения b все пары a, b будут иметь одинаковый хеш-код, независимо от значения a. Если b меньше нуля, ((a << 16) | b) == b из-за расширения знака. - person phoog; 14.04.2012
comment
Спасибо фуг. Я добавил заключенный в скобки UInt16. - person alan2here; 17.04.2012

GetHashCode не является (и не может быть) уникальным для каждого экземпляра объекта. Возьмите Int64, например; даже если хэш-функция идеально распределена, будет два четыре миллиарда Int64s, которые хешируют каждое значение, поскольку хэш-код, как вы упомянули, всего лишь Int32.

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

person goric    schedule 14.04.2012
comment
Если хэш-функция хорошо распределена, то, конечно, будет четыре миллиарда коллизий на 32-битный хэш. - person Eric Lippert; 14.04.2012
comment
Не 2 Int64s на Int32 хеш-значения, а 4294967296. 2^64 / 2^32 равно 2^32. - person phoog; 14.04.2012
comment
@EricLippert, phoog: конечно, ты прав. Что-то в моем мозгу увидело 32 и 64 и автоматически переключилось на 64/32, а не на 2^64/2^32... - person goric; 15.04.2012