2015-10-06 84 views
3

改性按照JavaDoc of java.util.HashSet.contains()方法假如果此集合包含指定的元素不以下HashSet.contains(object)返回例如后插入

返回true。更 正式,返回true,当且仅当此set包含元素e 这样(O == NULLé== 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方法返回trueHashSet.contains()返回false

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

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

+0

你改变了哈希,这被HashSet记住,因此它不能识别一个对象。 – Dims

+0

[HashSet包含自定义对象的问题]的可能重复(http://stackoverflow.com/questions/5110376/hashset-contains-problem-with-custom-objects) – SpaceTrucker

回答

6

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

有一对夫妇的事情,你可以在这里做:

  1. 修改的hashCode()所以它不会受你改变该领域的执行情况。假设这个字段对于对象的状态很重要,并且参与了方法,这有点代码味道,应该可以避免。

  2. 之前修改的对象,从集合中删除它,然后重新添加它一旦你完成修改它:


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

HashSet and HashMap use hashCode and equals methods to locate a object in its inner structure。 hashCode用于查找正确的存储桶,然后equals被用来区分具有相同哈希码的不同对象,因为后者不保证是唯一的。几乎在任何情况下,修改对象作为HashMap中的关键字或放入HashSet中都是非常糟糕的主意。如果这些修改更改了hashCode或equals方法的语义,则不会找到您的对象。

0

这是很清楚的,要添加到组之后,改变toInsert.v1,并且由于DemoClassv1v2属性获得的hashCode,也不会发现改变了哈希码elementes。

2

这是通过设计行为。

HashSet使用哈希来识别其持有的对象。

因此,如果您在将对象置于集合后更改它,则可能无法找到它。

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

我认为更好的方法是使用HashMap,其中明确分离可变和不可变的部分。