2011-06-05 45 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 
} 
相关问题