#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 и хэш-код.