关于java.util.Set.contains(Object o)
的{{3}}说:
当且仅当此集合包含元素e时才返回true (o == null?e == null:o.equals(e))。
那就是说,这是一个POJO(你可以看到,我覆盖了它的equals
方法):
public class MonthAndDay {
private int month;
private int day;
public MonthAndDay(int month, int day) {
this.month = month;
this.day = day;
}
@Override
public boolean equals(Object obj) {
MonthAndDay monthAndDay = (MonthAndDay) obj;
return monthAndDay.month == month && monthAndDay.day == day;
}
}
那么请问为什么以下代码会打印false
而不是true
?
Set<MonthAndDay> set = new HashSet<MonthAndDay>();
set.add(new MonthAndDay(5, 1));
System.out.println(set.contains(new MonthAndDay(5, 1)));
// prints false
解决方案是重写contains(Object o)
方法,但原始方法应该(几乎)完全相同,我错了吗?
Set<MonthAndDay> set = new HashSet<MonthAndDay>() {
private static final long serialVersionUID = 1L;
@Override
public boolean contains(Object obj) {
MonthAndDay monthAndDay = (MonthAndDay) obj;
for (MonthAndDay mad : this) {
if (mad.equals(monthAndDay)) {
return true;
}
}
return false;
}
};
set.add(new MonthAndDay(5, 1));
System.out.println(set.contains(new MonthAndDay(5, 1)));
// prints true
答案 0 :(得分:14)
当您覆盖equals(Object)
时,您还需要覆盖hashcode()
。
具体而言,必须实施这些方法,以便如果a.equals(b)
为true
,则a.hashcode() == b.hashcode()
全部为true
。如果不遵守此不变量,则HashMap
,HashSet
和Hashtable
将无法正常运行。
hashcode()
API中指定equals(Object)
和{{1}}的行为方式的技术细节。
那么,如果你弄错了,为什么基于哈希的数据结构会破坏?好吧,基本上是因为哈希表通过使用哈希函数的值来缩小要与“候选”进行比较的值集合。如果候选的哈希码与表中某个对象的哈希码不同,那么查找算法很可能不会与表中的对象进行比较......即使对象相等。
答案 1 :(得分:6)
HashSet
只会使用equals()
,如果元素共享相同的hashCode()
,那么您需要覆盖两者。 Here是HashSet#contains()
使用的代码的相关部分(请注意HashSet
由HashMap
支持):
355 /**
356 * Returns the entry associated with the specified key in the
357 * HashMap. Returns null if the HashMap contains no mapping
358 * for the key.
359 */
360 final Entry<K,V> getEntry(Object key) {
361 int hash = (key == null) ? 0 : hash(key.hashCode());
362 for (Entry<K,V> e = table[indexFor(hash, table.length)];
363 e != null;
364 e = e.next) {
365 Object k;
366 if (e.hash == hash &&
367 ((k = e.key) == key || (key != null && key.equals(k))))
368 return e;
369 }
370 return null;
371 }
不这样做,违反了Object#hashCode()
的合同,该合同声明:
如果根据
equals(Object)
方法两个对象相等,那么 在两个对象中的每一个上调用hashCode
方法必须生成 相同的整数结果。