Как мне реализовать Object.GetHashCode() для комплексного равенства?

StackOverflow https://stackoverflow.com/questions/1072058

Вопрос

В принципе, пока у меня есть следующее:

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.Плохая ли это идея, и если да, то как мне это сделать?

Это было полезно?

Решение

Я не думаю, что есть проблема с подходом, который вы выбрали.«Слишком сильное» беспокойство по поводу коллизий хэшей почти всегда является признаком чрезмерного обдумывания проблемы;пока хэш, скорее всего, будет другим, все будет в порядке.

В конечном итоге вы можете даже рассмотреть возможность исключения Description в любом случае из вашего хеша, если разумно ожидать, что в большинстве случаев объекты можно отличить по их названию и дате публикации (книги?).

Вы могли бы даже вообще игнорировать GUID в своей хеш-функции и использовать его только в Equals реализация для устранения неоднозначности в маловероятном (?) случае конфликтов хэшей.

Другие советы

Очень легко метод хеш-кода для пользовательских классов заключается в побитовом XOR каждого из хэш-кодов полей вместе.Это может быть так просто:

int hash = 0;
hash ^= this.Title.GetHashCode();
hash ^= this.PublishDate.GetHashCode();
hash ^= this.Description.GetHashCode();
return hash;

Из ссылка выше:

XOR имеет следующие приятные свойства:

  • Это не зависит от порядка вычислений.
  • Он не «тратит» биты.Если вы измените хотя бы один бит в одном из компонентов, итоговое значение изменится.
  • Это быстро, за один цикл даже на самом примитивном компьютере.
  • Он сохраняет равномерное распределение.Если две части, которые вы объединяете, распределены равномерно, такой же будет и комбинация.Другими словами, он не имеет тенденции сжимать диапазон дайджеста в более узкую полосу.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top