java.util.Set.contains(Object o)的奇怪行为

时间:2012-09-03 13:56:41

标签: java set equals contains

关于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

2 个答案:

答案 0 :(得分:14)

当您覆盖equals(Object)时,您还需要覆盖hashcode()

具体而言,必须实施这些方法,以便如果a.equals(b)true,则a.hashcode() == b.hashcode()全部为true。如果不遵守此不变量,则HashMapHashSetHashtable将无法正常运行。

hashcode() API中指定equals(Object)和{{1}}的行为方式的技术细节。


那么,如果你弄错了,为什么基于哈希的数据结构会破坏?好吧,基本上是因为哈希表通过使用哈希函数的值来缩小要与“候选”进行比较的值集合。如果候选的哈希码与表中某个对象的哈希码不同,那么查找算法很可能不会与表中的对象进行比较......即使对象相等。

答案 1 :(得分:6)

HashSet只会使用equals() ,如果元素共享相同的hashCode(),那么您需要覆盖两者。 HereHashSet#contains()使用的代码的相关部分(请注意HashSetHashMap支持):

  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方法必须生成   相同的整数结果。