java.util.Set.contains的怪异的行为(对象o)(Weird behavior o

2019-09-22 02:34发布

该DOC约java.util.Set.contains(Object o)说:

返回true当且仅当该组包含的元素e(O == NULLé== 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

Answer 1:

当你重写equals(Object) ,你也需要重写hashcode()

具体而言,这些方法必须被实现,这样,如果a.equals(b)true ,那么a.hashcode() == b.hashcode()是所有true 。 如果这个不变的不尊重,那么HashMapHashSetHashtable将无法正常工作。

怎样的技术细节hashcode()equals(Object)应该表现的是在指定对象的API。


那么,为什么,如果你得到这个错误的基于散列的数据结构打破? 嗯,基本上是因为哈希表的工作原理是使用散列函数来缩小组值的值与“候选人”进行比较。 如果考生的哈希码是对哈希码表中的某些对象的不同,那么机会是查找算法不会在表中的对象进行比较......即使对象是相等的。



Answer 2:

HashSet将只使用equals() ,如果元素共享相同hashCode()所以你需要同时重写。 这里的所使用的代码的相关部分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在各两个对象的方法必须产生相同的整数结果。



文章来源: Weird behavior of java.util.Set.contains(Object o)