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

java.util.Set.contains(对象o)的奇怪行为,java,set,equals,contains,Java,Set,Equals,Contains,aboutjava.util.Set.contains(对象o)说明: 当且仅当此集合包含元素e且 (o==null?e==null:o.equals(e)) 也就是说,这里有一个POJO(如您所见,我重写了它的equals方法): 那么,为什么下面的代码打印的是false,而不是true Set<MonthAndDay> set = new HashSet<MonthAndDay>(); set.add(new MonthAndDay(5, 1)); System.ou

about
java.util.Set.contains(对象o)
说明:

当且仅当此集合包含元素e且 (o==null?e==null:o.equals(e))

也就是说,这里有一个POJO(如您所见,我重写了它的
equals
方法):

那么,为什么下面的代码打印的是
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

HashSet
将仅在元素共享相同的
hashCode()
时使用
equals()
,因此您需要覆盖这两个元素s使用的代码的相关部分(请注意,
HashSet
HashMap
支持):

355/**
356*返回与中指定键关联的项
357*哈希映射。如果HashMap不包含映射,则返回null
358*的钥匙。
359        */
360最终输入getEntry(对象键){
361 int hash=(key==null)?0:hash(key.hashCode());
362(条目e=表[indexFor(hash,table.length)];
363 e!=零;
364 e=e.next){
365对象k;
366如果(e.hash==hash&&
367((k=e.key)==key | |(key!=null&&key.equals(k)))
368返回e;
369           }
370返回空值;
371       }
不这样做违反了的合同,该合同规定:

如果根据
equals(Object)
方法,两个对象相等,则 对两个对象中的每一个调用
hashCode
方法都必须产生 相同的整数结果


当您重写
equals(Object)
时,还需要重写
hashcode()

具体而言,必须实现这些方法,以便如果
a.equals(b)
true
,那么
a.hashcode()==b.hashcode()
都为
true
。如果不遵守此不变量,则
HashMap
HashSet
Hashtable
将无法正常工作

API中指定了
hashcode()
equals(Object)
应如何工作的技术细节



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

实际上HashSet使用
equals()
来比较具有相同
hashCode()的元素。
@PeterLawrey:你说得对,我昨晚在读你的博客文章时,你讨论了类似的内容:)。我已经编辑了我的答案。那么在我的例子中,您认为返回
month*100+day
hashCode()
如何?是的,它唯一地标识每个
MonthAndDay
对象
HashMap
(和
HashSet
)将重新刷新提供的
hashCode()
,以确保足够好的分发。在这里()看看Joshua Block关于实现
hashCode
s的建议。那么在我的例子中,您认为返回
month*100+day
hashCode()
如何?它应该可以正常工作。出于技术原因,如果将
100
替换为素数(例如31),效果会稍微好一些。然而,这不会有多大区别。
Set<MonthAndDay> set = new HashSet<MonthAndDay>();
set.add(new MonthAndDay(5, 1));
System.out.println(set.contains(new MonthAndDay(5, 1)));
// prints false
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
  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       }