2011-06-05 40 views
7

我用我自己的類作爲一個HashMap爲什麼自定義對象不是HashMap的等價鍵?

public class ActorId { 
    private final int playerId; 
    private final int id; 

    ActorId(int playerId, int id) { 
     this.playerId = playerId; 
     this.id = id; 
    } 

    public boolean equals(ActorId other) { 
     return this.id == other.id && this.playerId == other.playerId; 
    } 

    public int hashCode() { 
     int hash = 1; 
     hash = hash * 31 + playerId; 
     hash = hash * 31 + id; 
     return hash; 
    } 

    public String toString() { 
     return "#" + playerId + "." + id; 
    } 

    public int getPlayerId() { 
     return playerId; 
    } 
} 

這裏的關鍵有麻煩的是一個失敗的JUnit測試

import static org.junit.Assert.*; 
import java.util.Map; 
import org.junit.Test; 

public class ActorIdTest { 
    @Test 
    public final void testAsMapKey() { 
     ActorId a = new ActorId(123, 345); 
     ActorId b = new ActorId(123, 345); 

     assertTrue(a.equals(b)); 
     assertEquals(a.hashCode(), b.hashCode()); 

     // Works with strings as keys 
     Map<String, String> map1 = new java.util.HashMap<String, String>(); 

     map1.put(a.toString(), "test"); 
     assertEquals("test", map1.get(a.toString())); 
     assertEquals("test", map1.get(b.toString())); 
     assertEquals(1, map1.size()); 

     // But not with ActorIds 
     Map<ActorId, String> map2 = new java.util.HashMap<ActorId, String>(); 

     map2.put(a, "test"); 
     assertEquals("test", map2.get(a)); 
     assertEquals("test", map2.get(b)); // FAILS here 
     assertEquals(1, map2.size()); 

     map2.put(b, "test2"); 
     assertEquals(1, map2.size()); 
     assertEquals("test2", map2.get(a)); 
     assertEquals("test2", map2.get(b)); 
    } 
} 
+0

你說它在'... map2.get(b)'上失敗 - 你在Map中沒有這樣的鍵。你只向地圖添加了一個對象,即'a'實例。 – 2011-06-05 09:12:20

+0

@Björn是的,兩個ActorId對象是相等的並且具有相同的哈希碼,所以它們應該從地圖返回相同的值。 – dlundquist 2011-06-05 09:16:59

+0

呃,對不起!剛剛從牀上爬起來,應該已經讀完了整個代碼塊。 – 2011-06-05 09:18:28

回答

9

您需要更改

public boolean equals(ActorId other) { 
    .... 
} 

public boolean equals(Object other) { 
    .... 
} 

每日提示:始終使用@Override註釋。

如果您已經使用了@Override註釋,編譯器會陷入錯誤,並說:

方法等於類型的actorId的(的actorId)必須覆蓋或實現超方法

+1

@MGwynne - 您的評論有誤導性。 1)HashMap API **指定**使用「equal(Object)」。 2)簡單地改變'get'的簽名不會改變行爲。實現一個實際使用'boolean T.equals(K)'方法的'V get(K)'方法是不可能的,而不需要傳遞一個'類'對象並且使用反射來找到並調用equals方法。 – 2011-06-05 10:09:37

+0

@Stephen C - 你可以指向我指向HashMap API指定它使用equals(Object)的位置,而不是類型簽名?據我所知,get方法只是在對象上調用equals方法。我非常懷疑它明確地強調他們反對,因爲這已經由類型簽名給出了。唯一的一點是get方法在類型簽名*中指定它需要一個對象,因此重載的equals方法是隱藏的。如果簽名被更改爲'V get(K key)',那麼它會像預期的@dlundquist一樣工作。我誤解了什麼嗎? – MGwynne 2011-06-05 13:26:24

+0

當然,你不能簡單地去改變這個,也不是我建議的。也有很好的理由,基於Java的Object Equality概念,對於採用Object的簽名,我只是想指出爲什麼ActorId equals方法不會被調用,即使人們可能天真地認爲它會是。 – MGwynne 2011-06-05 13:26:36

3

您的代碼是正確的,但您也需要覆蓋從Object繼承的equals方法。

添加到您的ActorId類:

@Override 
public boolean equals(Object other) { 
    if(other == null || other.getClass() != getClass()) 
     return false; 
    return equals((ActorId)other); 
} 
1

你絕對必須覆蓋equals方法(對象),而對於某些實施一個Map(HashMap的),它也是necesary你overrdide方法的hashCode( )。

我有同樣的問題,沒有自定義hashCode實現類「ActorId」的equals方法從未被調用。

0

默認情況下的Java調用布爾equals(Object obj); 所以,你登錄是正確的,但如果你想重載equals()使用對象作爲參數,並通過instanceOfgetClass()檢查類和做一個類鑄件。

if (obj instanceOf ActorId) { 
    ActorId other = (ActorId)obj; 
    ... compare fields 
} 
相關問題