对于插入后修改的实例,HashSet.cons(object)返回false



根据java.util.HashSet.contains((的JavaDoc,该方法执行以下

如果此集合包含指定的元素,则返回true。更多形式上,当且仅当该集合包含元素e时返回true使得(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;
    }
}

打印输出:

错误

真实

因此,尽管equals方法返回true,但HashSet.contains()返回false

我想这是因为我在将toInsert实例添加到集合后修改了它。

然而,这并没有任何记录(或者至少我没能找到这样的记录(。此外,应该使用上面引用的equals方法的文档,但似乎不是这样。

当一个对象存储在HashSet中时,它会被放入一个数据结构中,该数据结构可以被对象的hashCode()轻松地(读取:高效地(搜索。修改对象可能会更改其hashCode()(取决于您如何实现它(,但不会更新它在HashSet中的位置,因为对象无法知道它包含在其中。

你可以在这里做几件事:

  1. 修改hashCode()的实现,使其不受正在更改的字段的影响。假设这个字段对对象的状态很重要,并且参与了equals(Object)方法,这在某种程度上是一种代码气味,应该避免。

  2. 在修改对象之前,将其从集合中删除,然后在完成修改后重新添加:


Set<DemoClass> mySet = ...;
DemoClass demo = ...;
boolean wasInSet = mySet.remove(demo);
demo.setV1("new v1");
demo.setV2("new v2");
if (wasInSet) {
    set.add(demo);
}

HashSetHashMap使用hashCodeequals方法在其内部结构中定位对象。hashCode用于查找正确的bucket,然后equals用于区分具有相同hashCode的不同对象,因为后者不能保证是唯一的。在几乎任何情况下,修改用作HashMap中的密钥或放入HashSet中的对象都是一个非常糟糕的想法。如果这些修改更改了hashCode或equals方法的语义,则将找不到您的对象。

这是由设计行为决定的。

HashSet使用散列来识别它所持有的对象。

所以,如果您在将一个对象放入集合后更改它,它可能无法找到它

您应该只持有不可变的对象,或者只使对象的那部分可变,这不会影响散列。

我认为最好使用HashMap,它清楚地将可变部分和不可变部分分开。

很明显,在添加到集合后,您正在更改toInsert.v1,并且由于DemoClassv1v2属性中获取hashCode,因此它不会为元素找到更改后的hashCode。

最新更新