HashSet.contains(object)返回false,例如在插入

时间:2015-10-06 06:23:47

标签: java collections hashset

根据JavaDoc of java.util.HashSet.contains()方法确实如下

  

如果此set包含指定的元素,则返回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方法文档,但似乎不是这样。

4 个答案:

答案 0 :(得分:8)

当一个对象存储在HashSet中时,它放入一个容易(读取:有效)的数据结构中,可以通过对象的hashCode()进行搜索。修改对象可能会更改其hashCode()(取决于您实现它的方式),但不会更新其在HashSet中的位置,因为对象无法知道它包含在一个中。

你可以在这里做几件事:

  1. 修改hashCode()的实施,使其不受您正在更改的字段的影响。假设这个字段对于对象的状态很重要,并且参与equals(Object)方法,这有点像代码味道,应该避免使用。

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


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

答案 1 :(得分:3)

HashSetHashMap使用hashCodeequals方法在内部结构中查找对象。 hashCode用于查找正确的存储桶,然后查询equals以区分具有相同hashCode的不同对象,因为后者不保证是唯一的。几乎在任何情况下,修改作为HashMap中的键或放入HashSet的键的对象是一个非常糟糕的主意。如果这些修改改变了hashCode或equals方法的语义,则无法找到您的对象。

答案 2 :(得分:2)

这是设计行为。

HashSet使用哈希来标识它所拥有的对象。

因此,如果在将对象放入集合后对其进行更改,则可能无法找到它。

你应该只保留不可变对象,或者只使一个对象的那部分可变,这不会影响哈希值。

我认为更好的是使用HashMap,它清楚地区分了可变部分和不可变部分。

答案 3 :(得分:0)

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