2015-10-06 69 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,其中明確分離可變和不可變的部分。