Как вычислить hashCode () из адреса объекта? - PullRequest
5 голосов
/ 24 сентября 2008

В Java у меня есть подкласс Vertex класса Java3D Point3f. Теперь Point3f вычисляет equals() на основе значений его координат, но для моего Vertex класса я хочу быть более строгим: две вершины равны, только если они являются одним и тем же объектом. Пока все хорошо:

class Vertex extends Point3f {

    // ...

    public boolean equals(Object other) {
        return this == other;
    }
}

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

Теперь, чтобы иметь возможность помещать вершины в HashMap, метод hashCode() должен возвращать результаты, соответствующие equals(). В настоящее время он делает это, но, вероятно, его возвращаемое значение основывается на полях Point3f, и, следовательно, будет давать коллизии хеш-функций для различных Vertex объектов с одинаковыми координатами.

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

Итак, на самом деле мой вопрос двоякий:

  • Должен ли я вообще хотеть такого мелкого equals()?
  • Если да, то как мне получить адрес объекта для вычисления хеш-кода?

Редактировать: Я просто подумал о чем-то ... Я мог бы генерировать случайное значение int при создании объекта и использовать его для хэш-кода. Это хорошая идея? Почему (нет)?

Ответы [ 7 ]

10 голосов
/ 24 сентября 2008

Либо используйте System.identityHashCode (), либо используйте IdentityHashMap.

1 голос
/ 24 сентября 2008

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

0 голосов
/ 25 сентября 2008

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

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

Выдержка из javadocs для Object.hashCode (): Насколько это практически целесообразно, метод hashCode, определенный классом Object, возвращает разные целые числа для разных объектов. (Это обычно реализуется путем преобразования внутреннего адреса объекта в целое число, но этот метод реализации не требуется языком программирования JavaTM.)

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

0 голосов
/ 24 сентября 2008

Почему вы хотите переопределить hashCode ()? Вы хотели бы сделать это, если вы хотите работать с некоторым другим определением равенства. Например

публичный класс А { int id;

public boolean equals (A other) {return other.id == id} public int hashCode () {return id;}

} где вы хотите прояснить, что если идентификаторы одинаковы, то объекты совпадают, и вы переопределяете хеш-код, чтобы вы не могли сделать это:

HashSet hash = new HashSet (); hash.add (новый A (1)); hash.add (новый A (1)); и получите 2 одинаковых (с точки зрения вашего определения равенства) A. Тогда правильное поведение будет таким, что у вас будет только 1 объект в хэше, вторая запись перезапишет.

0 голосов
/ 24 сентября 2008

Только к вашему сведению, ваш метод equals НЕ нарушает контракт equals (для контракта базового объекта) ... это в основном метод equals для метода base Object, поэтому, если вы хотите, чтобы тождество равнялось, а Vertex равнялся Это нормально.

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

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

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

0 голосов
/ 24 сентября 2008

Вы используете делегата, хотя этот ответ , вероятно, лучше.


class Vertex extends Point3f{
   private final Object equalsDelegate = new Object();
   public boolean equals(Object vertex){
      if(vertex instanceof Vertex){
         return this.equalsDelegate.equals(((Vertex)vertex).equalsDelegate);
      }
      else{
         return super.equals(vertex);
      }
   }
   public int hashCode(){
      return this.equalsDelegate.hashCode();
   }
}
0 голосов
/ 24 сентября 2008

Функция hashCode () унаследована от Object и работает точно так, как вы собираетесь (на уровне объекта, а не на уровне координат). Не должно быть необходимости менять его.

Что касается вашего метода equals, то нет никаких оснований даже использовать его, поскольку вы можете просто сделать obj1 == obj2 в своем коде вместо использования equals, поскольку он предназначен для сортировки и аналогичных операций, где сравнение координат много больше смысла.

...