Нужно ли реализовывать методы hashCode() и equals()?
Если у меня есть карта и объект в качестве ключа карты, достаточно ли хеш-метода по умолчанию и методов равенства?
class EventInfo{
private String name;
private Map<String, Integer> info
}
Затем я хочу создать карту:
Map<EventInfo, String> map = new HashMap<EventInfo, String>();
Должен ли я явно реализовывать hashCode() и equals()? Благодарю.
4 ответа
Да, вы делаете. HashMap
s работает, вычисляя хеш-код ключа и используя его в качестве базовой точки. Если hashCode
функция не переопределена (вами), тогда она будет использовать адрес памяти, и equals
будет так же, как ==
,
Если вы находитесь в Eclipse, он сгенерирует их для вас. Нажмите Меню исходного кода → Создать hashCode() и равно ().
Если у вас нет Eclipse, вот некоторые, которые должны работать. (Я создал их в Eclipse, как описано выше.)
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((info == null) ? 0 : info.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof EventInfo)) {
return false;
}
EventInfo other = (EventInfo) obj;
if (info == null) {
if (other.info != null) {
return false;
}
} else if (!info.equals(other.info)) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
Да, они вам нужны, иначе вы не сможете сравнить два EventInfo (и ваша карта не будет работать).
Зависит от того, что вы хотите, чтобы произошло. Если два разных EventInfo
экземпляры с тем же name
а также info
должно привести к двум разным ключам, тогда вам не нужно реализовывать equals
а также hashCode
,
Так
EventInfo info1 = new EventInfo();
info1.setName("myname");
info1.setInfo(null);
EventInfo info2 = new EventInfo();
info2.setName("myname");
info2.setInfo(null);
info1.equals(info2)
вернет ложь и info1.hashCode()
вернет другое значение info2.hashCode()
,
Поэтому, когда вы добавляете их на свою карту:
map.put(info1, "test1");
map.put(info2, "test2");
у вас будет две разные записи.
Теперь это может быть желаемое поведение. Например, если ваш EventInfo
собирает разные события, два разных события с одними и теми же данными могут захотеть быть двумя разными записями.
equals
а также hashCode
контракты также применимы в Set
,
Так, например, если информация о вашем событии содержит щелчки мыши, вполне может быть желательным, чтобы вы захотели получить в итоге:
Set<EventInfo> collectedEvents = new HashSet<EventInfo>();
collectedEvents.add(info1);
collectedEvents.add(info2);
2 собранные события вместо 1...
Надеюсь, у меня есть смысл здесь...
РЕДАКТИРОВАТЬ:
Однако, если вышеуказанный набор и карта должны содержать только одну запись, то вы можете использовать Apache Commons EqualsBuilder и HashCodeBuilder, чтобы упростить реализацию equals
а также hashCode
:
@Override
public boolean equals(Object obj) {
if (obj instanceof EventInfo) {
EventInfo other = (EventInfo) obj;
EqualsBuilder builder = new EqualsBuilder();
builder.append(name, other.name);
builder.append(info, other.info);
return builder.isEquals();
}
return false;
}
@Override
public int hashCode() {
HashCodeBuilder builder = new HashCodeBuilder();
builder.append(name);
builder.append(info);
return builder.toHashCode();
}
EDIT2:
Это также может быть целесообразно, если два EventInfo
экземпляры считаются одинаковыми, если они имеют одинаковые имена, например, если name
это некоторый уникальный идентификатор (я знаю, что он немного далек от вашего конкретного объекта, но я обобщаю здесь...)
Строго говоря, нет. Реализации по умолчанию hashCode() и equals() приведут к результатам, которые должны работать. См. http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html.
Насколько я понимаю, реализация по умолчанию hashCode() работает, беря адрес объекта в памяти и преобразуя его в целое число, а реализация по умолчанию equals() возвращает true, только если два объекта фактически являются одним и тем же объектом.
На практике вы могли (и должны), вероятно, улучшить обе эти реализации. Например, оба метода должны игнорировать не имеющие значения члены объекта. Кроме того, equals() может захотеть рекурсивно сравнить ссылки в объекте.
В вашем конкретном случае вы можете определить equals() как true, если два объекта ссылаются на одну и ту же строку или две строки равны и две карты одинаковы или они равны. Я думаю, что WChargin дал вам довольно хорошие реализации.