Как я должен идти о реализации Object.GetHashCode() для комплексного равенства?
На данный момент у меня есть следующее:
class Foo {
public override bool Equals(object obj)
{
Foo d = obj as Foo ;
if (d == null)
return false;
return this.Equals(d);
}
#region IEquatable<Foo> Members
public bool Equals(Foo other)
{
if (this.Guid != String.Empty && this.Guid == other.Guid)
return true;
else if (this.Guid != String.Empty || other.Guid != String.Empty)
return false;
if (this.Title == other.Title &&
this.PublishDate == other.PublishDate &&
this.Description == other.Description)
return true;
return false;
}
}
Итак, проблема заключается в следующем: у меня есть необязательное поле Guid
, который является уникальным идентификатором. Если это не установлено, тогда мне нужно попытаться определить равенство на основе менее точных метрик, чтобы попытаться определить, равны ли два объекта. Это отлично работает, но это делает GetHashCode()
грязный... Как я должен идти об этом? Наивная реализация будет выглядеть примерно так:
public override int GetHashCode() {
if (this.Guid != String.Empty)
return this.Guid.GetHashCode();
int hash = 37;
hash = hash * 23 + this.Title.GetHashCode();
hash = hash * 23 + this.PublishDate.GetHashCode();
hash = hash * 23 + this.Description.GetHashCode();
return hash;
}
Но каковы шансы столкновения двух типов хешей? Конечно, я не ожидал, что это будет 1 in 2 ** 32
, Это плохая идея, и если да, то как мне это сделать?
2 ответа
Я не думаю, что есть проблема с подходом, который вы выбрали для использования. Беспокойство о "слишком большом" количестве коллизий хешей почти всегда свидетельствует о том, что проблема слишком продумана. до тех пор, пока хеш, скорее всего, будет другим, у вас все будет хорошо.
В конечном счете, вы можете захотеть оставить Description
от вашего хэша в любом случае, если разумно ожидать, что большую часть времени объекты можно будет различить по их названию и дате публикации (книги?).
Вы можете даже игнорировать GUID в своей хеш-функции и использовать его только в Equals
реализация для устранения неоднозначного (?) случая хэш-конфликтов.
Очень простой метод хеширования для пользовательских классов - это поразрядно XOR каждого из хеш-кодов полей вместе. Это может быть так просто, как это:
int hash = 0;
hash ^= this.Title.GetHashCode();
hash ^= this.PublishDate.GetHashCode();
hash ^= this.Description.GetHashCode();
return hash;
По ссылке выше:
XOR обладает следующими приятными свойствами:
- Это не зависит от порядка вычисления.
- Он не "тратит впустую" биты. Если вы измените хотя бы один бит в одном из компонентов, окончательное значение изменится.
- Это быстро, один цикл даже на самом примитивном компьютере.
- Сохраняет равномерное распределение. Если две фигуры, которые вы объединяете, распределены равномерно, то и комбинация будет. Другими словами, он не имеет тенденцию сворачивать диапазон дайджеста в более узкую полосу.
XOR не работает должным образом, если вы ожидаете, что в ваших полях будут повторяться значения, так как повторяющиеся значения будут взаимно уничтожать друг друга, когда XORed. Поскольку вы хэшируете три несвязанных поля, в этом случае это не должно быть проблемой.