插入后更改值后,重复元素被添加到集合中

Duplicate elements being added in a set after I change the value after insertion

我有一个集合,其中添加了两个不同的对象。插入后,我以两个对象相等的方式更改其中一个对象(由对象 class 中重写的 equals 方法验证)。此时我在一个集合中有两个重复的元素。现在,我尝试将这两个重复对象添加到一个新集合中,即使 equals 方法 returns 对它们为真,我仍然能够添加它们。下面是相同的代码。有人可以告诉我我到底错过了什么吗?

public class BasicSetImpl{
public int num; String entry;
public BasicSetImpl(int num, String entry){
  this.num = num;
  this.entry = entry;
}

@Override
public int hashCode() {
  return Objects.hash(entry, num);
}

@Override
public boolean equals(Object obj) {
  BasicSetImpl newObj = (BasicSetImpl)obj;
  if (this.num == newObj.num)
    return true;
  else
    return false; 
}



public static void main(String[] args){
  Set<BasicSetImpl> set = new HashSet<>();
  BasicSetImpl k1 = new BasicSetImpl(1, "One");
  BasicSetImpl k2 = new BasicSetImpl(2, "Two");
  set.add(k1);
  set.add(k2);
  
  k2.num = 1;
  
  System.out.println(k1.equals(k2));  //This line returns True
  
  Set<BasicSetImpl> newSet = new HashSet<>();
  newSet.add(k1);
  newSet.add(k2);
  
  //Set.size here is two

基于散列的集合,在本例中 HashSet 使用对象 hashCode 方法计算散列值作为对象内容的函数。由于您同时考虑 entry 和 num 来确定对象的 hashcode 值,因此这两个对象具有不同的 hashcode,因为它们与 entry 不同。因此它们属于两个不同的哈希桶,永远不会被认为是相同的。

但是,如果您按如下方式设置条目

k2.entry = "One";

则k1和k2的hashcode值相同。那么它们都属于同一个哈希桶,并且根据 equals 方法两个对象是相同的。因此,现在忽略重复项。

但是,这里潜伏着一个问题。理想情况下,相等的对象必须具有相等的哈希码。但是,在你的情况下它不是。如果两个对象具有相同的 num 值,则它们是相等的,但它们可能会产生不同的 hashcode 值。所以正确的解决方案是按如下方式更改您的 hashCode。

@Override
public int hashCode() {
    return Integer.hashCode(num);
}

现在,如果没有我们通过设置 k2.entry = "One";

添加的 hack,它应该会按照您期望的方式运行

hashCodeequals方法必须一致。

来自documentation

If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

在您的情况下,尽管它们相等,但它们的 hashCode 是不同的。 添加元素时 HashSet 检查添加对象的哈希值,仅当集合中存在具有相同哈希值的现有对象时,HashSet 检查这些具有相同哈希值的对象是否相等。