2

我有一个有趣的情况,我将 a 存储CoordinateHashMap<Coordinate, GUIGameField>.

现在,奇怪的是,我有一段代码,它应该保护,不应该使用两次坐标。但是如果我调试这段代码:

if (mapForLevel.containsKey(coord)) {
    throw new IllegalStateException("This coordinate is already used!");
} else {
    ...do stuff...
}

...containsKey总是返回false,尽管我将哈希码为 9731 的坐标存储到地图中,并且当前坐标也具有哈希码 9731。

之后,mapForLevel.entrySet()看起来像:

(java.util.HashMap$EntrySet) [(270,90)=gui.GUIGameField@29e357, (270,90)=gui.GUIGameField@ca470]

我可能做错了什么?我没有主意了。谢谢你的帮助!

public class Coordinate {
    int xCoord;
    int yCoord;

    public Coordinate(int x, int y) {
        ...store params in attributes...
    }

    ...getters & setters...

    @Override
    public int hashCode() {
        int hash = 1;
        hash = hash * 41 + this.xCoord;
        hash = hash * 31 + this.yCoord;
        return hash;
    }
}
4

4 回答 4

5

您应该覆盖它equalshashCode使其正常工作。

编辑:我错误地说你应该hashCode在你的equals- 这是不正确的。虽然hashCode必须为两个相等的对象返回相同的结果,但它仍然可能为不同的对象返回相同的结果。

于 2012-10-14T15:32:29.087 回答
3

您似乎忘记equals()为您的坐标类实现方法。这是合同要求的。Hah 使用 equals 比较具有相同哈希码的 2 个条目。在您的情况下,Object.equals()对于 2 个不同的对象,调用总是不同的,因为它基于对内存中对象的引用。

于 2012-10-14T15:33:43.513 回答
1

您需要与 hashCode 一起实现 equals 的原因是哈希表的工作方式。

散列表将整数值(键的散列)与值相关联。将其视为一组 Value 对象。在此表中插入时,存储在key.hashCode()位置。

这使您可以“立即”找到表中的任何对象。您只需计算该对象的 hashCode,您就会知道它在桌子上的位置。把它想象成一棵树,你需要在树中导航才能找到对象)。

但是,这种方法存在一个问题:可能有多个对象具有相同的哈希码。这会导致您错误地将两个(或更多)键与相同的值相关联。这称为碰撞

有一种简单的方法可以解决这个问题:您可以将其映射到Key-Value对的列表,而不是将每个哈希码映射到一个Value

现在,每次您在哈希映射中查找对象时,在计算哈希后,您需要遍历该列表(与该哈希码相关的“值”列表)并找到正确的。

这就是为什么你总是需要在哈希映射的键上实现equals的原因。

注意:哈希表实际上比这复杂一点,但想法是一样的。您可以在此处阅读有关冲突解决的更多信息。

于 2012-10-14T16:08:57.540 回答
0

在你的类中定义hashCode方法。确保它为唯一对象返回唯一代码,并且为相同对象返回相同代码。Coordinate

于 2012-10-14T15:33:01.343 回答