Сомнение в хэш-коде Java

#java #hashcode

#java #хэш-код

Вопрос:

У меня есть эта программа:

 import java.util.*;
public class test {
    private String s;
    public test(String s) { this.s = s; }
    public static void main(String[] args) {
        HashSet<Object> hs = new HashSet<Object>();
        test ws1 = new test("foo");
        test ws2 = new test("foo");
        String s1 = new String("foo");
        String s2 = new String("foo");
        hs.add(ws1); 
        hs.add(ws2); 
        hs.add(s1); 
        hs.add(s2); // removing this line also gives same output.
        System.out.println(hs.size()); 
    } 
}
  

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

Приведенная выше программа выдает 3 в качестве выходных данных.

Кто-нибудь, пожалуйста, может объяснить, почему это так?

Я думаю (не уверен):

java.lang.String Класс переопределяет hashCode метод из java.lang.Object . Таким образом, String объекты со значением «foo» будут рассматриваться как дубликаты. Тестовый класс не переопределяет hashCode метод и в конечном итоге использует java.lang.Object версию, и эта версия всегда возвращает другой хэш-код для каждого объекта, поэтому два добавляемых тестовых объекта рассматриваются как разные.

Комментарии:

1. ваше понимание правильное.

2. Амир: Я предположил, что «java.lang. Версия объекта и эта версия всегда возвращает другой хэш-код для каждого объекта «. Как это может быть правдой. По принципу piegon hole, объектов может быть бесконечно, но хэш-код представляет собой ограниченное число без знака int. Таким образом, два разных объекта могут получить один и тот же хэш-код.

3. Не может быть бесконечных объектов, потому что у вас нет бесконечной памяти; Object.hashCode реализация использует адрес, по которому экземпляр хранится в памяти

4. Из документа: метод hashCode, определенный class Object, возвращает различные целые числа для разных объектов.

Ответ №1:

В данном случае речь идет не о hashCode() , а о equals() методе. По-прежнему установлен HashSet, смысл которого заключается в том, чтобы не допускать дубликатов. Дубликаты проверяются на использование equals() метода, который в случае строки вернет true

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

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

P.S. hashCode реализация Object не гарантирует уникальность значений. Это легко проверить с помощью простого цикла.

Комментарии:

1. «от объекта, который вернет true только тогда, когда оба объекта одинаковы» — ИМХО, лучше будет сказать «если ссылки совпадают»

2. Спасибо, я изменил формулировку

Ответ №2:

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

Ответ №3:

На самом деле, речь идет не о переопределении hashcode() , а о equals методе. Set не допускает дубликатов. Дубликат — это тот, в котором объекты логически равны.

Для проверки вы можете попробовать с

 System.out.println(ws1.equals(ws2));
System.out.println(s1.equals(s2));
  

Если объекты равны, набор примет только один.

Ответ №4:

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

  • equals(), hashCode() и toString() являются общедоступными.
  • Переопределите toString(), чтобы System.out.println() или другие методы могли видеть что-то полезное, например, состояние вашего объекта.
  • Используйте ==, чтобы определить, ссылаются ли две ссылочные переменные на один и тот же объект.
  • Используйте equals(), чтобы определить, являются ли два объекта значимо эквивалентными.
  • Если вы не переопределите equals(), ваши объекты не будут полезными ключами хэширования.
  • Если вы не переопределите equals(), разные объекты не могут считаться равными.
  • Строки и оболочки переопределяют equals() и создают хорошие ключи хеширования.
  • При переопределении функции equals() используйте оператор instanceof, чтобы убедиться, что вы оцениваете соответствующий класс.
  • При переопределении функции equals() сравните значимые атрибуты объектов.
  • Основные моменты контракта equals():
    a. Рефлексивный: x.equals(x) имеет значение true.
    b. Симметричный: если x.equals(y) истинно, то y.equals(x) должно быть истинным.
    c. Транзитивный: если значение x.equals(y) равно true, а значение y.equals(z) равно true, то значение z.equals(x) равно true.
    d. Согласованность: множественные вызовы x.equals(y) вернут один и тот же результат.
    e. Null: если x не равно нулю, то x.equals(null) равно false.
    f. Если x.equals(y) равно true, то x.hashCode() == y.hashCode() равно true.
  • Если вы переопределяете equals(), переопределите hashCode().
  • HashMap, HashSet, Hashtable, LinkedHashMap и LinkedHashSet используют хэширование.
  • Соответствующее переопределение хэш-кода() привязано к контракту hashCode().
  • Эффективное переопределение хэш-кода () равномерно распределяет ключи по своим сегментам.
  • Переопределенная функция equals() должна быть по крайней мере такой же точной, как и ее аналог hashCode().
  • Повторяю: если два объекта равны, их хэш-коды должны быть равны.
  • Для метода hashCode() допустимо возвращать одно и то же значение для всех экземпляров (хотя на практике это очень неэффективно).

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

В Commons есть хорошая реализация для EqualsBuilder и HashCodeBuilder. Они доступны на языке Coomons http://commons.apache.org/lang /

Я использую их, когда мне нужно реализовать equals и хэш-код.