2013-11-14 38 views
2

我在其中一个类中有以下方法。它只是一个围绕HashMap(名为teamOfPlayer,包含Player对象的键和Integer对象的值)的公共包装,仅此而已。HashMap.get()使用继承的hashCode()方法正常工作,无法用用户定义的hashCode()方法识别密钥

public int getTeamOfPlayer(Player p) 
{  
    return teamOfPlayer.get(p); 
} 

只要我的Player对象从Object继承默认的hashCode()方法,这工作得很好。但是,为了将我的Pl​​ayer对象保存到文件中,我实现了自己的hashCode()。突然间,该方法开始抛出一个NullPointerException。

我扩展了下面的方法来打印出一些调试信息,但这让我更加困惑。

public int getTeamOfPlayer(Player p) 
{ 
    Object[] o = teamOfPlayer.keySet().toArray(); 
    Player p2 = (Player) o[0]; 

    System.out.println("getTeamOfPlayer(" + p + ")" 
     + "\n\thash of argument is " + p.hashCode() 
     + "\n\tkeySet() of hashmap is " + teamOfPlayer.keySet() 
     + "\n\tcontainsKey() of hashmap is " + teamOfPlayer.containsKey(p) 
     + "\n\tplayer extracted from keySet() is " + p2 
     + "\n\tplayer extracted from keySet() has hash of" + p2.hashCode() 
     + "\n\targument.equals(key) returns " + p.equals(p2) 
     + "\n\tkey.equals(argument) returns " + p2.equals(p)); 

    int i = teamOfPlayer.get(p); 
    return i; 
} 

上述方法的输出是在这里:

getTeamOfPlayer([email protected]) 
    hash of argument is -1980353825 
    keySet() of hashmap is [[email protected]] 
    containsKey() of hashmap is false 
    player extracted from keySet() is [email protected] 
    player extracted from keySet() has hash of-1980353825 
    argument.equals(key) returns true 
    key.equals(argument) returns true 

的抛出异常的 “INT I = teamOfPlayer.get(P);”这意味着地图返回null(因为它不认为它包含关键字)。我知道这就是抛出异常的原因。不过,我认为我已经证明了地图中存在的关键因素。这是怎么回事?

-

更新: 这里是equals()和hashCode()方法。

@Override 
public boolean equals(Object obj) 
{ 
    if (this == obj) 
     return true; 

    Player player; 

    if (obj != null && obj instanceof Player) 
     player = (Player) obj; 
    else 
     return false; 

    if (status != player.status || !name.equals(player.name) || race != player.race || weeksOut != player.weeksOut || injuryType != player.injuryType 
     || XP != player.XP) 
     return false; 

    for (int i = 0; i < 8; i++) 
    { 
     if (attributes[i] != player.attributes[i]) 
      return false; 

     if (injuries[i] != player.injuries[i]) 
      return false; 
    } 

    for (int i = 0; i < 28; i++) 
    { 
     if (hasSkill[i] != player.hasSkill[i]) 
      return false; 
    } 

    for (int i = 0; i < 4; i++) 
    { 
     if (equipment[i] != player.equipment[i]) 
      return false; 
    } 

    return true; 
} 

@Override 
public int hashCode() 
{ 
    int hash = 11; 

    hash = 31 * hash + status; 
    hash = 31 * hash + name.hashCode(); 
    hash = 31 * hash + race; 
    hash = 31 * hash + weeksOut; 
    hash = 31 * hash + injuryType; 
    hash = 31 * hash + XP; 

    for (int i = 0; i < 8; i++) 
    { 
     hash = 31 * hash + attributes[i]; 
     hash = 31 * hash + injuries[i]; 
    } 

    for (int i = 0; i < 28; i++) 
     hash = hash + (hasSkill[i] ? 1 : 0); 

    for (int i = 0; i < 4; i++) 
     hash = 31 * hash + equipment[i]; 

    return hash; 
} 
+5

你是否重写'equals'以与hashCode一致? – rgettman

+1

请发布您的hashCode()和equals()方法的代码 – NickJ

+1

@rgettman您可以从调试输出中看到,不仅两个对象具有相同的哈希码,而且它们也相互平等。 –

回答

1

发生了什么事是你自动拆箱一null

Map的值类型是Integer,不int,而get方法返回一个null要么是因为关键是没有找到,或者因为映射条目实际上有它的价值null

的代码行多数民众赞成爆炸:

int i = teamOfPlayer.get(p); 

实际上被编译为:

int i = teamOfPlayer.get(p).intValue(); 

从包装Integer,这可能是null转换,对原始int,这可能不是null

你必须处理空值,例如尝试在无效的情况下,赋予一个默认值i

Integer value = teamOfPlayer.get(p); 
int i = value == null ? 0 : value; 
+0

不错的简洁解释 –

1

这两个答案都是正确的。更具体地说,

teamOfPlayer.get(p)返回null(可能是因为equals()和hashcode()不同步) 然后getTeamOfPlayer()试图将null转换为int。

如果该方法返回一个整的不是int,你不会有这样的问题,或者如果您编码它作为

public int getTeamOfPlayer(Player p) 
{  
    Integer t = teamOfPlayer.get(p); 
    if (t == null) { 
     return -1; 
    } 
    return t; 
} 

你会好起来的。 可能你需要做两个。无论如何,修正hashCode(),而且,你可能需要考虑一个不在任何团队中的玩家的合法情况。

0

看到equals() and hashCode() contract in Java

与测试的问题是,它只能证明有一个对玩家为其hashCode()和equals()方法返回true。 你还没有证明对于地图中所有可能的球员对(a,b),如果a.equals(b)则a.hashCode()== b.hashCode()。你也许可以写这样一个测试,并找到断开连接的位置。

这很重要的原因是因为它也会影响向地图添加条目的过程。这就是为什么你的hashCode()方法迫使问题出现。如果您获得java.lang.Object的源代码,您将在注释中看到默认的hashCode实现很可能在计算hashCode时使用该对象的ADDRESS。这与默认的equals()方法一起被定义为a == b。

不过,看到违反合同的地方并不容易,那就是a.equals(b)但是a.hashCode()!= b.hashCode()。

一个可能的地方,我不能肯定地说,因为我不知道该类型的所有变量有:

if (status != player.status || !name.equals(player.name) || race != player.race || weeksOut != player.weeksOut || injuryType != player.injuryType 
    || XP != player.XP) 
    return false; 

如果其中任何一个都是布尔值,这可能不会给你你期待的结果。您可能需要在每个条件周围放置括号,或者更好地使每个条件成为单独的条件,并在每个不平等条件下返回错误。

换句话说,问题很可能是对equals()的误判。

相关问题