2011-05-04 17 views
6

我知道還有其他有關一般最佳實踐的問題,但是在使用hashCode和equals時卻存在一些問題,但我有一個非常具體的問題。在Java中針對特定情況重寫hashCode

我有一個類作爲一個實例變量,一個相同類的數組。爲了更加明確,下面的代碼:

Class Node{ 
    Node arr[] = new Node[5]; 
} 

我需要覆蓋的hashCode爲類節點,和數組是確定兩個節點是否是相同的一個重要的,決定性的因素。我怎樣纔能有效地將數組合併到hashCode的計算中?

- 編輯 -

我想檢查兩個節點是相同的,這意味着它們具有相同數量的孩子,和那些孩子們帶來完全一樣的狀態。因此,我正在有效地嘗試比較兩個節點上的子樹。我想知道如果我可以使用散列做這種平等檢查。

我想我真的需要散列整個子樹,但我不知道我怎麼會去這樣做給我的類定義的遞歸性。

+2

我假設你建立的節點樹在某個時刻終止並且不是無限的? – justkt 2011-05-04 17:36:55

+0

是的,它終止。 – efficiencyIsBliss 2011-05-04 17:45:29

+1

@efficiencyIsBliss - 只要沒有子節點引用父節點,您應該可以在數組上使用deepHashCode和deepEquals。 – justkt 2011-05-04 18:18:00

回答

4

包含http://download.oracle.com/javase/6/docs/api/java/util/Arrays.html#hashCode(java.lang.Object [])作爲hashCode()實現的一部分。

+0

我剛剛編輯了這個問題,以更準確地反映我的需求。鑑於我想檢查子樹的平等性,我不認爲我可以使用您引用的方法。另請參見另一個名爲deepHashCode()的方法,但描述表明它不能用於包含其自身的數組,但我不確定數組是否包含它自己。 – efficiencyIsBliss 2011-05-04 17:48:44

+0

@efficiencylsBliss如果您不能保證節點之間沒有遞歸/循環引用,那麼您將冒無限循環的風險。交付的Java方法不檢查這些情況。 – JVerstry 2011-05-04 18:10:54

1

這取決於您的平等標準。數組中的訂單重要嗎?如果是這樣,你可能想要使哈希碼依賴於數組中節點的順序。如果不是的話,你可能想要做一些操作,比如對數組中所有節點的散列碼進行異或。據推測,其中一些值可能爲空(所以要小心)。

基本上,您需要一致覆蓋hashCodeequals,以便如果兩個對象相同,則它們將具有相同的散列碼。這是黃金法則。

埃裏克利珀有great blog post about GetHashCode in .NET - 建議同樣適用於爲Java。

要注意的一個潛在問題 - 如果最終在節點中出現一個循環(節點B的數組中出現節點A的引用,反之亦然),則最終可能會在散列代碼中產生循環也計算。

1

您可以使用Arrays.hashCode()Arrays.equals()方法。

2

我想檢查兩個節點 是相同的,這意味着它們具有 相同數量的孩子,那 那些孩子導致完全相同的 狀態。因此,我試圖比較 兩個節點上的子樹, 。我想知道是否可以使用 哈希來執行此平等檢查。

不,散列不應該被用來檢查平等。這不是它的目的。它最終可以幫助你發現物體是否不相等,但如果它們是平等的,它就不會告訴你任何東西。

相同的對象將產生相同的散列值,但是兩個不同的對象,其不相等也可以產生相同的散列。換句話說,如果散列值不同,你肯定知道對象是不同的。而已。

如果你想測試平等,你需要實現等於。在你的情況下,你的方法會遞歸併引發堆棧溢出。如果你的對象包含對自身的引用呢?

如果你想生成一個散列,你可以考慮數組的大小(以及它是否爲空的事實),但我不會嘗試使用數組中的對象的散列值,因爲潛在的無限循環。這並不完美,但它足夠好。

還有另一個激進的方法,可能會提供良好的結果。不是動態計算哈希值,而是爲每個Node對象實例設置一個隨機的int值(我的意思是在創建時爲所有人創建一次,並始終返回該值)。在你的情況下,你不會冒險數組中的對象實例的哈希值的風險無限循環。

如果散列值相等,則需要開始比較數組對象實例。

REM:如果節點包含其他屬性,那麼計算這些其他屬性的散列並忘記數組。開始調查數組內容/大小當且僅當散列在兩個對象之間是相同的。

REM2:評論提到DAG圖表,這意味着我們不會遇到遞歸性問題。但是,該條件不足以保證deepHashCode()將成功。而且,這也太過分了。有一個更有效的方法來解決這個問題。

如果由節點使用的散列方法只有使用該數組來計算散列值,那麼deepHashCode()可能工作。但這不會有效。如果散列方法使用其他節點屬性,那麼這些屬性也必須相同。

有一種更快的方式來比較節點的相等性。用一個唯一的數字標記每個節點實例。然後,爲了比較兩個節點,首先比較它們的數組大小。如果它是等於,則使用其唯一編號比較每個陣列中的節點。如果一個數組沒有「擁有」另一個節點,那麼我們沒有處理相同的節點。這個解決方案比遞歸更快。

+0

我不同意哈希不應該用於平等檢查。事實上,關於散列的Javadoc明確指出,如果根據equals()方法兩個對象相等,那麼它們必須散列到同一事物。 – efficiencyIsBliss 2011-05-04 18:10:38

+0

@efficiencyIsBliss - 事情是,[兩個不相等的對象可以*也具有相同的hashCode](http://download.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode( ))。 – justkt 2011-05-04 18:23:11

+0

的確如此。我猜,我讀得不夠徹底。 – efficiencyIsBliss 2011-05-04 18:25:55

0

如果性能有任何問題,可以添加幾個要添加到當前答案的點。

首先,你需要決定是否爲節點中子節點的順序。如果他們不這樣做,則不能將哈希碼用於數組。考慮圍繞由java.util.Set定義的哈希碼函數來構建哈希碼函數。另外考慮在內部使用一些排序來提高等同性能。例如,如果子樹的深度/高度不同,可以按深度排序。

其次,如果你的子樹很深,你的散列碼可能會非常昂貴。所以我會緩存哈希碼,並在構建時計算它(如果您的節點是不可變的),或者在變化時無效並按需重新計算。第三,如果你的子樹很深,請檢查equals()中的hashcode並提前返回false。是的,hashcode是通過Map實現來檢查的,但有些地方代碼只是使用equals()比較兩個對象,而且它們可能會付出很高的代價。最後,考慮使用數組。asList()(如果子訂單很重要)或HashSet(如果排序無關緊要,並且沒有兩個子節點相等)而不是簡單的數組。然後equals和hashcode被簡化爲將調用委託給容器實例......當然,適當地緩存hashcode。