如果未覆盖hashCode(),则HashSet允许重复项目插入

时间:2012-06-20 07:22:27

标签: java

class temp {
int id;

public int getId() {
  return id;
}

temp(int id) {
  this.id = id;
}

public void setId(int id) {
  this.id = id;
}

@Override
public boolean equals(Object obj) {
  if (this == obj)
      return true;
  if (obj == null)
      return false;
  if (getClass() != obj.getClass())
      return false;
  temp other = (temp) obj;
  if (id != other.id)
      return false;
  return true;
}
}

public class testClass {

    public static void main(String[] args) {
      temp t1 = new temp(1);
      temp t2 = new temp(1);
      System.out.println(t1.equals(t2));
      Set<temp> tempList = new HashSet<temp>(2);
      tempList.add(t1);
      tempList.add(t2);
      System.out.println(tempList);
}

程序将两个元素添加到Set中。我首先感到震惊,因为在添加方法时,调用了equals方法。

但后来我覆盖了hashCode方法:

@Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + id;
        return result;
    }

然后它没有添加。这是令人惊讶的,因为Set和add()方法的Javadoc说它在添加到Set时只检查equals()。

这是add()的javadoc:

/**
     * Adds the specified element to this set if it is not already present.
     * More formally, adds the specified element <tt>e</tt> to this set if
     * this set contains no element <tt>e2</tt> such that
     * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
     * If this set already contains the element, the call leaves the set
     * unchanged and returns <tt>false</tt>.
     *
     * @param e element to be added to this set
     * @return <tt>true</tt> if this set did not already contain the specified
     * element
     */
    public boolean add(E e) {
      return map.put(e, PRESENT)==null;
    }

然后我意识到HashSet是作为HashMap实现的,在地图中,对象的hashCode用作键。因此,如果你不重写hashCode,它会使用不同的键来处理它们。

这不应该在add()方法或HashSet的文档中吗?

4 个答案:

答案 0 :(得分:20)

有点记录。请参阅hashCode()的文档,其中Object.equals(Object)

  

如果两个对象根据equals(Object)方法相等,那么   在两个对象中的每一个上调用 hashCode方法必须生成   相同的整数结果。

此外,在instanceA.equals(instanceB) == true方法的文档中可以找到以下内容:

  

请注意,通常需要覆盖hashCode方法   每当重写此方法时,都要保持一般   hashCode方法的合同,声明相等的对象必须   有相同的哈希码

换句话说,如果您的班级在instanceA.hashCode() != istanceB.hashCode()和{{1}}时实际上违反了Object类的合同。

答案 1 :(得分:14)

只需查看equals()文档:

  

请注意,通常需要在重写此方法时覆盖hashCode方法,以便维护hashCode方法的常规协定,该方法声明相等对象必须具有相等的哈希码

事实是equals()hashCode()有很强的联系。在处理其中一个时,应始终考虑这两个问题,以避免这些一致性问题。

答案 2 :(得分:8)

如果覆盖equals(),必须覆盖hashCode()。

对equals()和hashCode()的行为有一些限制,它们在Object的文档中枚举。特别是,equals()方法必须具有以下属性:

  • 对称性:对于两个引用,a和b,a.equals(b)当且仅当b.equals(a)
  • 自反性:对于所有非空引用,a.equals(a)
  • 及物性:如果a.equals(b)和b.equals(c),则a.equals(c)
  • 与hashCode()的一致性:两个相等的对象必须具有相同的hashCode()值

有关详细信息,请参阅this

答案 3 :(得分:1)

他们(javadoc人)可能已经预先假设他们说(在add()中的HashSet方法的文档中)

(e==null ? e2==null : e.equals(e2))

hashCode()对于他们两个来说都是固有的。