2

我的一门课中有以下方法。它只是一个 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);”上抛出异常 行,这意味着地图返回 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;
}
4

3 回答 3

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(),但你可能需要考虑一个不属于任何团队的球员的合法情况。

于 2013-11-14T20:38:36.670 回答
1

发生的事情是您正在自动拆箱null.

Map 的值类型是Integer, not int,并且 get 方法返回 anull要么是因为找不到键,要么是因为 Map 条目实际上有 anull作为其值。

爆炸的代码行:

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;
于 2013-11-14T20:31:58.567 回答
0

参见Java 中的 equals() 和 hashCode() 合约

您的测试的问题在于,它仅证明有对玩家的 hashcode() 和 equals() 返回 true。 您尚未证明对于地图中所有可能的玩家对(a,b),如果 a.equals(b) 则 a.hashCode() == b.hashCode()。您可能可以编写这样的测试并找到断开连接的位置。

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

尽管如此,还是很难看出你违反了这个合同的地方,那就是 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() 的误报。

于 2013-11-14T22:57:31.777 回答