2011-04-21 53 views
1

我在使用Java Collections API時遇到此問題。基本上這是一個實現Kruskal算法尋找MST的支持方法。我創建了這個類來實現union/find算法。Java集合API HashSet刪除方法

我的問題,因爲我能找到解決辦法,是否有人知道爲什麼「聯合」方法中的remove方法不能一致地工作。這是在運行時它會刪除一些元素而不是其他元素。例如,我爲了一個涉及城市的任務實施了這個任務,似乎並不喜歡去除一些城市。特別是它偶然偶然發現了幾套不同的套裝,但總是同樣的套裝。我想知道這是否是一個對象引用問題,也就是說我是否在測試錯誤的東西,但我無法繞過它。

我知道我的其餘工作是正確的,因爲我可以用消除元素的循環替換它,並且算法執行完美。然而,可能會有稍差的表現。

我想知道是否有人能看到一個錯誤。另外我應該注意到,我從不同的類中調用它,但是,調用是使用find方法檢索的元素進行的。請注意,find方法必須正常工作,因爲只需更改remove方法就可以使整個工作正常工作,即找到並返回適當的對象。

感謝

奧斯卡

/* 
* A constructor for creating a new object of this class. 
*/ 
DisjointSets() 
{ 
    underlying = new HashSet<HashSet<String>>(); 
} 

/* 
* A method for adding a set to this DisjointSets object 
*/ 
void add(HashSet<String> h) 
{ 
    underlying.add(h); 
} 

/* 
* A method for finding an element in this DisjointSet object. 
*/ 
HashSet<String> find(String s) 
{ 
    // Check each set in the DisjointSets object 
    for(HashSet<String> h: underlying) 
    { 
     if(h.contains(s)) 
     { 
      return h; 
     } 
    } 
    return null; 
} 

/* 
* A method for combining to subsets of the DisjointSets 
*/ 
void union(HashSet<String> h1, HashSet<String> h2) 
{ 
    System.out.print("CHECK ON DS\n"); 
    System.out.print("*********************\n"); 
    System.out.print("H1 is : { "); 
    for (HashSet<String> n: underlying) 
    { 
     System.out.print("Set is : { "); 
     for (String h : n) 
     { 
      System.out.print(h + " , "); 
     } 
     System.out.print("} \n "); 
    } 
    // Add the objects of h1 to h2 
    // DOES NOT WORK CONSISTENTLY 
      h1.addAll(h2); 
    underlying.remove(h2); 
} 

}

和我一起

HashSet<HashSet<String>> temp = new HashSet<HashSet<String>>(); 
     for(HashSet<String> f: underlying) 
     { 
      if(f != h2) 
      { 
       temp.add(f); 
      } 
     } 
     underlying = temp; 
+0

@lwburk感謝您的格式幫助,讚賞。 – oscarcollings 2011-04-21 21:39:21

回答

4

問題取代它的是,當你修改嵌套HashSets的內容之一,你搞砸了外部HashSet的內部(因爲嵌套的hashCode()編輯HashSet已更改)。爲了正確維護這個集合,每當你想修改其中一個嵌套HashSet時,你必須首先從外部HashSet中移除它,然後重新添加它(如果需要的話)。

(你真的沒有提供足夠的代碼來確定這是否真的是問題,但這是我最好的猜測)。

Set<Set<String>> outerSet = new HashSet<String>(); 
Set<String> innerSet = new HashSet<String>(); 

innerSet.add("foo"); 
outerSet.add(innerSet); 

// *** BROKEN *** 
innerSet.add("bar");  // <- adding element to innerSet changes result of innerSet.hashCode() 
outerSet.remove(innerSet); // <- this may or may not work because outerSet is _broken_ 
// *** BROKEN *** 

// *** CORRECT *** 
outerSet.remove(innerSet); 
innerSet.add("bar"); 
// now you can put innerSet back in outerSet if necessary 
+0

因此要清楚修改HashSet類的對象的內容還會修改該類的hashCode()方法?我只是想澄清你的答案,因爲你似乎說你必須從外部HashSet中移除它。你的意思是基礎?或者你的意思是包含該對象的基礎子集? (直覺上我會認爲這是內在的)。我只是對您指定的刪除順序稍有困惑。想知道更多,並感謝回答。 – oscarcollings 2011-04-21 21:42:26

+0

@oscarcollings - 對不起,隨着所有的遊戲集合在一起,很難澄清我在說什麼。我會添加一些代碼示例。 – jtahlborn 2011-04-22 11:01:25

+0

哦,是的,這是有道理的。我現在看到,爲了保持散列函數的完整性,你必須採取內部設置。感謝您的幫助,這個例子確實有助於澄清事情。 – oscarcollings 2011-04-23 02:38:33

0

上@ jtahlborn的答案的後續行動,爲AbstractSet.hashCode()合同說

返回此 集的哈希碼值。集合的哈希碼定義爲 爲該集合中的 元素的哈希碼的總和。這確保 s1.equals(s2)意味着 s1.hashCode()== s2.hashCode()對於任何 兩個集合s1和s2,這是Object.hashCode的 常規協定的要求。

此實現了 枚舉集,調用集合中的每個元素的hashCode方法 和 加起來的結果。

代碼來演示@ jtahlborn的回答(這是正確的)

import java.util.HashSet; 
import java.util.Set; 


public class TestHashSetHashCode { 

    public static void main(String[] args) 
    { 
    Set<String> strings = new HashSet<String>(); 
    strings.add("one"); 
    strings.add("two"); 
    strings.add("three"); 
    strings.add("four"); 
    strings.add("five"); 
    Set<String> test = new HashSet<String>(); 
    System.out.println("Code "+test.hashCode()); 
    for (String s : strings) { 
     test.add(s); 
     System.out.println("Code "+test.hashCode()); 
    } 
    } 
} 

輸出

 
Code 0 
Code 115276 
Code 3258622 
Code 3368804 
Code 113708290 
Code 116857384 

一個理由添加到列表中儘量使用一成不變的館藏儘可能。