Вопрос

В 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 значение при создании объекта и используйте его для хэш-кода.Это хорошая идея?Почему нет)?

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

Решение

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

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

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

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


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();
   }
}

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

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

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

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

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

открытый класс A {int id;

Public Boolean Equals (другой) {return ore.id == id} public int hashcode () {return id;}

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

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

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

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

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

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

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

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

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