Учитывая следующий ключ:
int key = Guid.NewGuid().GetHashCode();
Является ли этот ключ уникальным как уникальность Guid?
Учитывая следующий ключ:
int key = Guid.NewGuid().GetHashCode();
Является ли этот ключ уникальным как уникальность Guid?
принцип пигментации говорит нет. GUID имеет 16 байт информации - 128 бит. int
содержит 32 бита информации. (EDIT: Чтобы уточнить из-за комментариев,.NET GUID позволит установить эти 128 бит произвольно, насколько мне известно, случайно созданные GUID следуют более строгим шаблонам, поэтому не существует 2 128 различные значения, которые были бы случайным образом сгенерированы. Тем не менее, более 2 32.)
Существует 2 128 возможных GUID и 2 32 возможных хеш-кодов - поэтому у вас не может быть другого хэш-кода для каждого GUID.
Там более того, хотя - GetHashCode()
никогда не предназначено для представления уникальности. Если это возможно, то это здорово - но это не обязательно, даже если для этого достаточно доступных значений int
.
Было бы вполне справедливо для int.GetHashCode()
возвращать (скажем) значение, деленное на два... так что -1, 0 и 1 будут получать хэш-код 0; 3 и 4 получили бы хэш-код 2 и т.д. Это было бы нехорошо (и это было бы медленнее, чем просто возврат значения), но это была бы действительная реализация. Он удовлетворяет всем ограничениям GetHashCode
, а именно: если вы назовете его двумя равными значениями, он вернет тот же хэш-код.
Фактически, возвращение константы для всех значений является допустимой реализацией - хотя и довольно бесполезной, поскольку она обеспечивает нормальный поиск хэш-таблицы в операции O (N).
GetHashCode()
возвращает целое число - оно не может быть таким же уникальным, как Guid
, поэтому нет - могут быть столкновения и уникальность не гарантируется.
Точка хеш-кода состоит в том, что она должна распределяться равномерно по хэш-диапазону, чтобы столкновения были, как правило, редкими, у вас всегда есть шанс столкновения, хотя и для этого нужно учитывать.
Как раз сегодня я заметил еще одну проблему с Guid.GetHashCode()
: в реализации Microsoft.NET не каждый байт Guid
хэширован: есть 6 байтов Guid
, которые не хешированы, поэтому любое изменение одного из них никогда не изменит хэш-код.
Мы можем увидеть его в справочном источнике:
return _a ^ (((int)_b << 16) | (int)(ushort)_c) ^ (((int)_f << 24) | _k);
поэтому байты _d
, _e
, _g
, _h
, _i
, _j
не хэшируются. Это имеет важное значение для "последовательного" Guid
s, например:
c482fbe1-9f16-4ae9-a05c-383478ec9d13
c482fbe1-9f16-4ae9-a05c-383478ec9d14
c482fbe1-9f16-4ae9-a05c-383478ec9d15
...
c482fbe1-9f16-4ae9-a05c-383478ec9dff
c482fbe1-9f16-4ae9-a05c-383478ec9e00
c482fbe1-9f16-4ae9-a05c-383478ec9e01
с Guid
, как и у них, количество генерируемых хэшей очень невелико (256 различных значений), потому что 3478ec9d
/3478ec9e
не будет хэшироваться.
A Guid - это 128-битное число. Int - это 32-битное число, поэтому оно не может быть "уникальным", как "Guid".
Кроме того, GetHashCode возвращает... хэш-код, он не должен быть уникальным. См. Другие обсуждения здесь о SO о том, почему GetHashCode() существует.
У меня была именно проблема xanatos описывает в другом ответе. У меня есть класс, где два значения Guid
используются для различения разных объектов, и я обнаружил, что получаю ужасное количество столкновений (мои Гиды не генерируются случайным образом). Вот код, который я использовал для решения проблемы. Guid1
и Guid2
- это свойства типа Guid
, которые различают объекты. Код следует подход, описанный Джоном Скитом здесь.
public override int GetHashCode()
{
int hash = 173;
foreach (Byte b in Guid1.ToByteArray().Concat(Guid2.ToByteArray()))
{
hash = hash * 983 + b;
}
return hash;
}