Hashcode и Equals для Hashset
Прошу прояснить мои сомнения в Hashset. Рассмотрим следующий код:
class Person
{
String name;
Person(String n)
{
name=n;
}
public String getName()
{
return name;
}
@Override
public boolean equals(Object arg0) {
System.out.println("in equals");
Person obj=(Person)arg0;
System.out.println("1st "+getName());
System.out.println("2nd "+obj.getName());
if(this.getName().equals(obj.getName()))
{
return true;
}
return false;
}
@Override
public int hashCode() {
System.out.println("in hash code");
System.out.println(" value is "+Integer.valueOf(name.charAt(0)));
return Integer.valueOf(name.charAt(0));
}
}
В основном у меня есть следующий код
Person obj1=new Person("bcd");
Person obj2=new Person("cde");
Person obj3=new Person("abc");
Person obj4=new Person("abc");
Теперь, если я добавлю эти объекты в hashset
Set<Person> sset=new HashSet<Person>();
sset.add(obj1);
sset.add(obj4);
sset.add(obj2);
sset.add(obj3);
Я получаю этот вывод
in hash code
value is 98
in hash code
value is 97
in hash code
value is 99
in hash code
value is 97
in equals
1st abc
2nd abc
Вопрос 1: почему функция equals() вызывается только один раз для проверки obj3 и obj4? Почему его не проверяют на предмет остальной части объектов?
Вопрос 2. Если ответ состоит в том, что оба они имеют один и тот же хеш-код, то будет вызываться только тогда равно, тогда почему его не вызывают для кода ниже
sset.add(obj1);
sset.add(obj4);
sset.add(obj2);
sset.add(obj4);
:
in hash code
value is 98
in hash code
value is 97
in hash code
value is 99
in hash code
value is 97
Он не идет внутрь метода equals(), даже если к хэш-набору добавляются два одинаковых объекта, которые имеют одинаковый хэш-код.
Вопрос 3: я повторил вышеуказанное значение и распечатал содержимое, но не были вызваны ни хэш-код, ни равно. когда его действительно полезно переопределить метод hashcode и equals?
Вопрос 4: Когда вызывается hashCode()
и equals()
?
Ответы
Ответ 1
- Нет необходимости вызывать equals, если
hashCode
отличается
- Нет необходимости вызывать
hashCode
, если (obj1 == obj2)
- Нет необходимости в
hashCode
и/или равно для итерации - вы не сравниваете объекты
- При необходимости различать объекты.
Ответ 2
Думаю, на ваши вопросы будет дан ответ, если вы поймете, как работают Set, и в частности HashSets. Набор представляет собой набор уникальных объектов, причем Java определяет уникальность в том, что он не равен ничему другому (равно возвращает false).
HashSet использует хэш-коды для ускорения работы. Он предполагает, что два объекта, которые будут равны друг другу, будут иметь один и тот же хэш-код. Однако он не предполагает, что два объекта с одним и тем же хэш-кодом означают, что они равны. Поэтому, когда он обнаруживает встречный хэш-код, он сравнивается только с другими объектами (в вашем случае один) в наборе с тем же хэш-кодом.
Ответ 3
в соответствии с исходным кодом jdk с javasourcecode.org, HashSet использует HashMap как свою внутреннюю реализацию, код о методе put из HashSet ниже:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
Правило сначала проверяет хэш, затем проверяет ссылку, а затем вызывается метод равенства объекта.
Ответ 4
Вы должны прочитать, как обеспечить правильную реализацию equals и hashCode. Это хорошая отправная точка: Какие проблемы следует учитывать при переопределении equals и hashCode в Java?
Ответ 5
Потому что во втором случае вы добавляете одну и ту же ссылку дважды, а HashSet
проверяете это в HashMap.put()
, на которой основана HashSet
:
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
Как вы можете видеть, equals
будет вызываться только в том случае, если хеш добавляемого ключа равен ключу, уже присутствующему в наборах и, ссылки этих двух разных.
Ответ 6
Откажитесь от HashSet со всеми его методами, и вы увидите, как это работает.