HashSet.contains(object) returns false 例如插入后修改
HashSet.contains(object) returns false for instance modified after insertion
根据JavaDoc of java.util.HashSet.contains()方法做如下
Returns true if this set contains the specified element. More
formally, returns true if and only if this set contains an element e
such that (o==null ? e==null : o.equals(e)).
然而,这似乎不适用于以下代码:
public static void main(String[] args) {
HashSet<DemoClass> set = new HashSet<DemoClass>();
DemoClass toInsert = new DemoClass();
toInsert.v1 = "test1";
toInsert.v2 = "test2";
set.add(toInsert);
toInsert.v1 = null;
DemoClass toCheck = new DemoClass();
toCheck.v1 = null;
toCheck.v2 = "test2";
System.out.println(set.contains(toCheck));
System.out.println(toCheck.equals(toInsert));
}
private static class DemoClass {
String v1;
String v2;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((v1 == null) ? 0 : v1.hashCode());
result = prime * result + ((v2 == null) ? 0 : v2.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DemoClass other = (DemoClass) obj;
if (v1 == null) {
if (other.v1 != null)
return false;
} else if (!v1.equals(other.v1))
return false;
if (v2 == null) {
if (other.v2 != null)
return false;
} else if (!v2.equals(other.v2))
return false;
return true;
}
}
打印出来:
false
true
所以虽然equals
方法returnstrue
,HashSet.contains()
returnsfalse
.
我想这是因为我在将 toInsert 实例添加到集合中之后对其进行了修改。
然而,这绝不是记录在案的(或者至少我找不到这样的记录)。也应该使用 equals 方法上面引用的文档,但它似乎不是这样。
HashSet
和 HashMap
使用 hashCode
和 equals
方法在其内部结构中定位对象。 hashCode
用于找到正确的桶,然后查询 equals
以区分具有相同 hashCode 的不同对象,因为后者不能保证是唯一的。在几乎任何情况下,修改用作 HashMap
中的键或放入 HashSet
中的对象都是一个非常糟糕的主意。如果这些修改更改了 equals
方法的 hashCode 或语义,则将找不到您的对象。
很明显,你在添加到集合后改变toInsert.v1
,由于DemoClass
从v1
和v2
属性中获取hashCode,它赢了找不到元素的已更改哈希码。
这是设计使然的行为。
HashSet
使用散列来识别它持有的对象。
因此,如果您在将某个对象放入集合后对其进行更改,则可能无法找到它。
您应该只持有不可变对象,或者只使对象的那部分可变,这不会影响散列。
我认为使用 HashMap
更好,它明确区分了可变部分和不可变部分。
当一个对象存储在 HashSet
中时,它被放入一个数据结构中,该数据结构可以很容易地(读取:有效地)被对象的 hashCode()
搜索到。修改一个对象可能会改变它的 hashCode()
(取决于你如何实现它),但不会更新它在 HashSet
中的位置,因为对象无法知道它包含在一个中。
您可以在这里做几件事:
修改 hashCode()
的实现,使其不受您正在更改的字段的影响。假设这个字段对对象的状态很重要,并且参与 equals(Object)
方法,这有点代码味道,应该避免。
在修改对象之前,将其从集合中删除,然后在完成修改后重新添加它:
Set<DemoClass> mySet = ...;
DemoClass demo = ...;
boolean wasInSet = mySet.remove(demo);
demo.setV1("new v1");
demo.setV2("new v2");
if (wasInSet) {
set.add(demo);
}
根据JavaDoc of java.util.HashSet.contains()方法做如下
Returns true if this set contains the specified element. More formally, returns true if and only if this set contains an element e such that (o==null ? e==null : o.equals(e)).
然而,这似乎不适用于以下代码:
public static void main(String[] args) {
HashSet<DemoClass> set = new HashSet<DemoClass>();
DemoClass toInsert = new DemoClass();
toInsert.v1 = "test1";
toInsert.v2 = "test2";
set.add(toInsert);
toInsert.v1 = null;
DemoClass toCheck = new DemoClass();
toCheck.v1 = null;
toCheck.v2 = "test2";
System.out.println(set.contains(toCheck));
System.out.println(toCheck.equals(toInsert));
}
private static class DemoClass {
String v1;
String v2;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((v1 == null) ? 0 : v1.hashCode());
result = prime * result + ((v2 == null) ? 0 : v2.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DemoClass other = (DemoClass) obj;
if (v1 == null) {
if (other.v1 != null)
return false;
} else if (!v1.equals(other.v1))
return false;
if (v2 == null) {
if (other.v2 != null)
return false;
} else if (!v2.equals(other.v2))
return false;
return true;
}
}
打印出来:
false
true
所以虽然equals
方法returnstrue
,HashSet.contains()
returnsfalse
.
我想这是因为我在将 toInsert 实例添加到集合中之后对其进行了修改。
然而,这绝不是记录在案的(或者至少我找不到这样的记录)。也应该使用 equals 方法上面引用的文档,但它似乎不是这样。
HashSet
和 HashMap
使用 hashCode
和 equals
方法在其内部结构中定位对象。 hashCode
用于找到正确的桶,然后查询 equals
以区分具有相同 hashCode 的不同对象,因为后者不能保证是唯一的。在几乎任何情况下,修改用作 HashMap
中的键或放入 HashSet
中的对象都是一个非常糟糕的主意。如果这些修改更改了 equals
方法的 hashCode 或语义,则将找不到您的对象。
很明显,你在添加到集合后改变toInsert.v1
,由于DemoClass
从v1
和v2
属性中获取hashCode,它赢了找不到元素的已更改哈希码。
这是设计使然的行为。
HashSet
使用散列来识别它持有的对象。
因此,如果您在将某个对象放入集合后对其进行更改,则可能无法找到它。
您应该只持有不可变对象,或者只使对象的那部分可变,这不会影响散列。
我认为使用 HashMap
更好,它明确区分了可变部分和不可变部分。
当一个对象存储在 HashSet
中时,它被放入一个数据结构中,该数据结构可以很容易地(读取:有效地)被对象的 hashCode()
搜索到。修改一个对象可能会改变它的 hashCode()
(取决于你如何实现它),但不会更新它在 HashSet
中的位置,因为对象无法知道它包含在一个中。
您可以在这里做几件事:
修改
hashCode()
的实现,使其不受您正在更改的字段的影响。假设这个字段对对象的状态很重要,并且参与equals(Object)
方法,这有点代码味道,应该避免。在修改对象之前,将其从集合中删除,然后在完成修改后重新添加它:
Set<DemoClass> mySet = ...;
DemoClass demo = ...;
boolean wasInSet = mySet.remove(demo);
demo.setV1("new v1");
demo.setV2("new v2");
if (wasInSet) {
set.add(demo);
}