Ответ
Да, если два объекта равны по equals(), они должны оставаться равными, пока их состояние, участвующее в сравнении, не изменится. Нарушение этого правила ведет к некорректной работе коллекций, основанных на хэшах (HashMap, HashSet).
Пример нарушения с изменяемым (mutable) объектом:
public final class MutableKey {
private int id;
public MutableKey(int id) { this.id = id; }
public void setId(int id) { this.id = id; } // Сеттер меняет состояние!
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MutableKey)) return false;
MutableKey that = (MutableKey) o;
return id == that.id; // Сравнение по полю `id`
}
@Override
public int hashCode() { return Objects.hash(id); }
}
// Демонстрация проблемы
MutableKey key1 = new MutableKey(1);
MutableKey key2 = new MutableKey(1);
Set<MutableKey> set = new HashSet<>();
set.add(key1);
System.out.println(set.contains(key2)); // true (объекты равны)
key1.setId(2); // Меняем состояние объекта, уже лежащего в множестве!
System.out.println(set.contains(key1)); // false! Объект "потерялся"
// Хэш-бакет изменился, но объект не перераспределился.
Решение:
- Используйте неизменяемые (immutable) объекты в качестве ключей (
String,Integer, собственные immutable классы). - Если объект должен быть изменяемым, не изменяйте поля, участвующие в
equals()/hashCode(), после помещения объекта в hash-коллекцию.