Какова цель Equals в IEqualityComparer [duplicate]

Если у вас есть , существующий ID ( говорит из существующего набора данных ), то вполне нормально переопределять _id с тем, который у вас есть.

]

... keeo _id нетронутый, поскольку он помогает индексировать и т. д.

MongoDB по умолчанию индексирует поле _id. Если вы начнете вкладывать целые числа в поле _id, они будут индексироваться, как и все остальное.

Таким образом, большинство RDBM предоставляют идентификатор «auto-increment». Это хорошо для небольших наборов данных, но действительно плохо с точки зрения масштабируемости. Если вы пытаетесь вставить данные на 20 серверов одновременно, как вы сохраняете «автоматическое увеличение»?

Обычный ответ заключается в том, что вы этого не делаете. Вместо этого вы в конечном итоге используете такие же идентификаторы GUID для этих идентификаторов. В случае MongoDB ObjectId уже предоставлен.

Мне было интересно узнать о какой-либо лучшей стратегии, чтобы можно было использовать объект mongoIt как более удобный для пользователя и легко запоминающийся ключ

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

Итак, вам нужно сделать компромисс здесь. Если у вас есть таблица, которая может стать действительно большой, я предлагаю использовать ObjectId. Если у вас есть относительно небольшая таблица и она часто не обновляется (например, таблица поиска), вы можете создать собственный автоинкремент.

Выбор действительно зависит от вас.

1176
задан Irshad 18 September 2015 в 10:46
поделиться

12 ответов

Да, важно, чтобы ваш элемент использовался как ключ в словаре, или HashSet<T> и т. д. - поскольку он используется (при отсутствии пользовательского IEqualityComparer<T>) для группировки элементов в ведра. Если хэш-код для двух элементов не соответствует, они могут never считаться равными (Equals просто никогда не будет вызываться).

Метод GetHashCode() должен отражать логика Equals; следующие правила:

  • , если две вещи равны (Equals(...) == true), то они должны возвращать одно и то же значение для GetHashCode()
  • , если GetHashCode() равен, для них необходимо не , чтобы они были одинаковыми; это столкновение, и Equals будет вызываться, чтобы убедиться, что это реальное равенство.

В этом случае это выглядит так: «return FooId;» является подходящим GetHashCode(). Если вы тестируете несколько свойств, обычно их объединяют с использованием кода, как показано ниже, для уменьшения диагональных столкновений (т. Е. Для new Foo(3,5) имеет другой хеш-код для new Foo(5,3)):

int hash = 13;
hash = (hash * 7) + field1.GetHashCode();
hash = (hash * 7) + field2.GetHashCode();
...
return hash;

Ох - для удобства вы можете также рассмотреть возможность предоставления операторов == и != при переопределении Equals и GetHashCode.


Демонстрация того, что происходит, когда вы ошибаетесь здесь .

1110
ответ дан Community 4 September 2018 в 06:51
поделиться

Просто добавьте следующие ответы:

Если вы не переопределяете Equals, то по умолчанию используется сравнение ссылок объектов. То же самое относится к hashcode - приведение по умолчанию обычно основано на адресе памяти ссылки. Поскольку вы переопределили Equals, это означает, что правильное поведение заключается в сравнении того, что вы внедрили в Equals, а не в ссылках, поэтому вы должны сделать то же самое для hashcode.

Клиенты вашего класса ожидают, что хэш-код будет иметь схожую логику с методом equals, например, методы linq, которые используют IEqualityComparer, сначала сравнивают хэш-коды и только если они равны, они будут сравнивать Equals ( ), который может быть более дорогим для запуска, если бы мы не реализовали hashcode, у равного объекта, вероятно, будут разные хэш-коды (потому что они имеют другой адрес памяти) и будут ошибочно определены как не равные (Equals () даже не попадут ).

Кроме того, кроме проблемы, что вы не сможете найти свой объект, если вы использовали его в словаре (потому что он был вставлен одним хэш-кодом, и когда вы его ищете, хэш-код по умолчанию будет вероятно, будет отличаться, и снова Equals () даже не будет вызван, как объясняет Марк Гравелл в своем ответе, вы также вводите нарушение концепции словаря или хешета, которая не должна допускать идентичные ключи - вы уже заявили, что эти объекты по существу то же самое, когда вы переопределяете Equa поэтому вы не хотите, чтобы оба они были разными ключами в структуре данных, которые предполагают наличие уникального ключа. Но поскольку у них есть другой хэш-код, «тот же» ключ будет вставлен как другой.

8
ответ дан BornToCode 4 September 2018 в 06:51
поделиться

Ниже, используя отражение, кажется мне лучшим вариантом с учетом общедоступных свойств, так как с этим вам не нужно беспокоиться о добавлении / удалении свойств (хотя это не так распространенный сценарий). Это, как мне показалось, лучше работает. (По сравнению с секундомером).

    public int getHashCode()
    {
        PropertyInfo[] theProperties = this.GetType().GetProperties();
        int hash = 31;
        foreach (PropertyInfo info in theProperties)
        {
            if (info != null)
            {
                var value = info.GetValue(this,null);
                if(value != null)
                unchecked
                {
                    hash = 29 * hash ^ value.GetHashCode();
                }
            }
        }
        return hash;  
    }
-5
ответ дан Guanxi 4 September 2018 в 06:51
поделиться

Это не обязательно важно; это зависит от размера ваших коллекций и требований к производительности, и будет ли ваш класс использоваться в библиотеке, где вы не можете знать требования к производительности. Я часто знаю, что размеры моей коллекции не очень большие, и мое время более ценно, чем несколько микросекунд производительности, достигнутых благодаря созданию идеального хеш-кода; поэтому (чтобы избавиться от раздражающего предупреждения компилятором) я просто использую:

   public override int GetHashCode()
   {
      return base.GetHashCode();
   }

(Конечно, я мог бы использовать #pragma, чтобы выключить предупреждение, но я предпочитаю этот способ.)

Если вы находитесь в том положении, в котором вы делаете , то, конечно, требования к производительности, чем все проблемы, упомянутые другими, применяются здесь. Самое главное - иначе вы получите неправильные результаты при извлечении элементов из хеш-набора или словаря: хеш-код не должен меняться в зависимости от времени жизни объекта (точнее, в течение времени, когда требуется хэш-код, например, ключ в словаре): например, следующее неверно, поскольку значение является общедоступным и поэтому может быть изменено извне в класс во время жизни экземпляра, поэтому вы не должны использовать его в качестве основы для хеш-кода:


   class A
   {
      public int Value;

      public override int GetHashCode()
      {
         return Value.GetHashCode(); //WRONG! Value is not constant during the instance's life time
      }
   }    

С другой стороны, если значение не может быть изменено, это нормально использовать:


   class A
   {
      public readonly int Value;

      public override int GetHashCode()
      {
         return Value.GetHashCode(); //OK  Value is read-only and can't be changed during the instance's life time
      }
   }

3
ответ дан ILoveFortran 4 September 2018 в 06:51
поделиться

На самом деле очень сложно реализовать GetHashCode() правильно, потому что, помимо уже упомянутых правил Marc, хэш-код не должен меняться в течение всего жизненного цикла объекта. Поэтому поля, которые используются для вычисления хэш-кода, должны быть неизменными.

Наконец-то я нашел решение этой проблемы, когда я работал с NHibernate. Мой подход заключается в вычислении хеш-кода из идентификатора объекта. Идентификатор может быть установлен только с помощью конструктора, поэтому, если вы хотите изменить ID, что очень маловероятно, вам нужно создать новый объект с новым идентификатором и, следовательно, новый хэш-код. Этот подход лучше всего работает с GUID, потому что вы можете предоставить конструктор без параметров, который произвольно генерирует ID.

118
ответ дан Jim McKeeth 4 September 2018 в 06:51
поделиться

Это связано с тем, что фреймворк требует, чтобы два объекта, которые одинаковы, должны иметь один и тот же хэш-код. Если вы переопределите метод equals, чтобы выполнить специальное сравнение двух объектов, и оба объекта считаются одинаковыми с помощью метода, то хэш-код двух объектов также должен быть одинаковым. (Словари и Hashtables полагаются на этот принцип).

9
ответ дан kemiller2002 4 September 2018 в 06:51
поделиться

Хэш-код используется для коллекций на основе хэша, таких как Dictionary, Hashtable, HashSet и т. д. Цель этого кода - очень быстро предварительно сортировать конкретный объект, помещая его в определенную группу (ведро). Эта предварительная сортировка очень помогает в поиске этого объекта, когда вам нужно вернуть его из коллекции хешей, потому что код должен искать ваш объект только в одном ведро, а не во всех его объектах. Лучшее распределение хэш-кодов (лучшая уникальность) - более быстрое извлечение. В идеальной ситуации, когда каждый объект имеет уникальный хеш-код, поиск его является операцией O (1). В большинстве случаев он приближается к O (1).

5
ответ дан Maciej D. 4 September 2018 в 06:51
поделиться
7
ответ дан Malachi 4 September 2018 в 06:51
поделиться

Пожалуйста, не забудьте проверить параметр obj против null при переопределении Equals(). А также сравните тип.

public override bool Equals(object obj)
{
    if (obj == null || GetType() != obj.GetType())
        return false;

    Foo fooItem = obj as Foo;

    return fooItem.FooId == this.FooId;
}

Причиной этого является: Equals должен возвращать false при сравнении с null. См. Также http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx

32
ответ дан Matthew Murdoch 4 September 2018 в 06:51
поделиться

Завершая Equals, вы в основном заявляете, что именно тот, кто лучше знает, как сравнивать два экземпляра данного типа, так что вы, вероятно, будете лучшим кандидатом для предоставления лучшего хеш-кода.

Это пример того, как ReSharper пишет для вас функцию GetHashCode ():

public override int GetHashCode()
{
    unchecked
    {
        var result = 0;
        result = (result * 397) ^ m_someVar1;
        result = (result * 397) ^ m_someVar2;
        result = (result * 397) ^ m_someVar3;
        result = (result * 397) ^ m_someVar4;
        return result;
    }
}

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

41
ответ дан Nisarg Shah 4 September 2018 в 06:51
поделиться

Как насчет:

public override int GetHashCode()
{
    return string.Format("{0}_{1}_{2}", prop1, prop2, prop3).GetHashCode();
}

Предполагая, что производительность не является проблемой:)

24
ответ дан shA.t 4 September 2018 в 06:51
поделиться

Я понимаю, что исходный GetHashCode () возвращает адрес памяти объекта, поэтому необходимо переопределить его, если вы хотите сравнить два разных объекта.

EDITED: это неверно, исходный метод GetHashCode () не может гарантировать равенство двух значений. Хотя объекты, которые равны, возвращают один и тот же хэш-код.

0
ответ дан user2855602 4 September 2018 в 06:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: