我的一个课程中有以下方法。它只是一个围绕HashMap的公共包装器(名为teamOfPlayer,具有Player对象的键和Integer对象的值),仅此而已。
public int getTeamOfPlayer(Player p)
{
return teamOfPlayer.get(p);
}
只要我的Player对象从Object继承了默认的hashCode()方法,这就可以了。但是,为了将我的Player对象保存到文件,我实现了自己的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(main.data.entities.Player@89f632df)
hash of argument is -1980353825
keySet() of hashmap is [main.data.entities.Player@89f632df]
containsKey() of hashmap is false
player extracted from keySet() is main.data.entities.Player@89f632df
player extracted from keySet() has hash of-1980353825
argument.equals(key) returns true
key.equals(argument) returns true
在“int i = teamOfPlayer.get(p);”上抛出异常line,表示地图返回null(因为它不认为它包含密钥)。我知道这就是抛出异常的原因。但是,我想我已经证明地图中存在关键的DOES。发生了什么事?
-
更新: 这是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;
}
答案 0 :(得分:1)
您正在自动取消装箱null
。
Map的值类型为Integer
,而不是int
,并且get方法返回null
,因为找不到密钥或者Map条目实际上有null
1}}为它的价值。
爆炸的代码行:
int i = teamOfPlayer.get(p);
实际编译为:
int i = teamOfPlayer.get(p).intValue();
从包装Integer
(可能是null
)转换为原始int
,可能不是null
。
你必须处理null,例如在null的情况下尝试给i
一个默认值:
Integer value = teamOfPlayer.get(p);
int i = value == null ? 0 : value;
答案 1 :(得分:1)
这两个答案都是正确的。更具体地说
teamOfPlayer.get(p)返回null(可能是因为equals()和hashcode()不同步) 然后getTeamOfPlayer()尝试将null转换为int。
如果该方法返回Integer而不是int,则不会出现此问题,或者如果您将其编码为
public int getTeamOfPlayer(Player p)
{
Integer t = teamOfPlayer.get(p);
if (t == null) {
return -1;
}
return t;
}
你会没事的。
可能你需要做两件事。无论如何,修复hashCode(),但是,您可能需要考虑不在任何团队中的玩家的合法情况。
答案 2 :(得分: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;
如果其中任何一个是布尔值,这可能无法提供您期望的结果。您可能需要在每个条件周围加上括号,或者更好的是,使每个条件成为一个单独的条件,并在每个不等式时返回false。
换句话说,问题很可能是对equals()的误报。