14

我正在尝试在 HashMap 中找到一个键。我可以使用“get”打印选定的键,但是当我在 if 语句中使用“containsKey”时,找不到它。

我知道密钥存在于地图中,但它一直返回错误。任何想法的人?

我的代码:

public static boolean checkLowerStructuralSupport(Location location) {

    boolean hasSupport = false;

    Location supportingLocation = new Location(location.getX(), location.getY(), location.getZ() - 1);

    System.out.println(_levels.get(supportingLocation.getZ()).getLevelSites2().get(supportingLocation)); //works

    if (_levels.get(supportingLocation.getZ()).getLevelSites2().containsKey(supportingLocation)) {
        hasSupport = true;
    } else {
        hasSupport = false;
    }

    return hasSupport;
}

这是 Location 类的代码:

public class Location {

    protected int _x;
    protected int _y;
    protected int _z;

    public Location(int xAxis, int yAxis, int zAxis) {
        this._x = xAxis;
        this._y = yAxis;
        this._z = zAxis;
    }

    public void equals() {
        //not implemented yet
    }

    public void HashCode() {
        //not implemented yet
    }

    public String toString() {
        String locationString = Integer.toString(_x) + Integer.toString(_y) + Integer.toString(_z);
        return locationString;
    }

    public void setX(int XAxis) {
        this._x = XAxis;
    }

    public int getX() {
        return this._x;
    }

    public void setY(int YAxis) {
        this._y = YAxis;
    }

    public int getY() {
        return this._y;
    }

    public void setZ(int ZAxis) {
        this._z = ZAxis;
    }

    public int getZ() {
        return this._z;
    }

}
4

9 回答 9

23

您必须确保Location该类已正确实现其hashCode()equals(Object)方法(文档)。也就是说,如果两个Location对象实际上相等,它们应该共享一个公共哈希码并且它们的equals方法应该返回true

于 2009-07-09T13:56:42.870 回答
5

如此处所述,您必须重写equals(Object)方法。

get(Object) 工作的原因是,HashMap 将为您的 Location 类计算 Hash 并返回 hascode 指向的 Object。

containsKey(Object) 计算哈希键并获取哈希指向的对象。HashMap 中的对象将与您放入的对象进行比较。对于这些比较,使用 equals 方法。当你不重写他的equals方法时,当对象引用同一个实例时,返回true。

来自 HashMap

/** 
 * Check for equality of non-null reference x and possibly-null y. 
 */
static boolean eq(Object x, Object y) {
    return x == y || x.equals(y);
}

从对象

public boolean equals(Object obj) {
    return (this == obj);
    }

来自equals的javadoc

Object 类的 equals 方法实现了对象上最有区别的可能等价关系;也就是说,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象(x == y 的值为 true)时,此方法才返回 true。

请注意,每当重写该方法时,通常都需要重写 hashCode 方法,以维护 hashCode 方法的一般约定,即相等的对象必须具有相等的哈希码。

于 2009-07-09T14:06:51.580 回答
2

Location类中,确保您覆盖了hashCodeequals方法。

如果你是,你能发布它们吗?

于 2009-07-09T13:57:56.223 回答
2

containsKey 使用 equals 方法将参数与键集中的条目进行比较。所以 Location 类需要有一个好的 equals 方法。java.lang.Object 中的默认 equals 方法仅在两个对象是同一个对象时才返回 true。在这种情况下,您可能需要比较 2 个不同的实例,并且需要一个自定义的 equals 方法。

于 2009-07-09T13:59:43.257 回答
2

我能想到的唯一会导致这种情况的是,如果 of 的状态supportingLocationget(...)调用和containsKey(...).

假设您发布的代码片段是导致问题的确切代码,唯一可能发生这种情况的地方是Location#getZ(...)Location#hashCode()Location#equals(Object)改变 Location 的状态(或 Location 构造函数,或这些方法之一启动一个随机更改状态的线程Location 实例,但我认为我们可以排除这种情况)。

您能否验证上述方法都没有改变supportingLocation实例的状态?虽然我不熟悉Location类本身,但我敢猜测这样的类在理想情况下是不可变的。

编辑:澄清一下,当我说Location#getZ()etc 没有改变位置时,我的意思是:

Location x = new Location(1,2,3);
Location y = new Location(1,2,3);

boolean eq1 = x.equals(y);
int hash1 = x.hashCode();
x.getZ(); // this should *not* mutate the state of x
boolean eq2 = x.equals(y);
int hash2 = x.hashCode();

最后,eq1 应该等于 eq1,hash1 应该等于 hash2。如果不是这种情况, getZ() 正在改变 x 的状态(或等于,或 hashCode,或更糟糕的是,这些方法完全关闭),并将导致您观察到的行为。

于 2009-07-09T14:34:50.520 回答
1

两者get()containsKey()使用Location类的hashCode()方法。equals()除非存在哈希冲突,否则不会调用该方法。(因此,HashMap 的 get() 不会equals()在所有情况下都使用。)

对于您的Location班级,您是否碰巧实现了自己的版本hashCode()?该hashCode()方法应谨慎执行。Joshua Bloch 在《 Effective Java》一书中描述了所有细节,其中部分内容已在线发布……我将找到这些示例章节的链接: Effective Java Sample Chapters。你想要第3章。

正如我在对该问题的评论中所问的那样,您的_levels变量来自哪里?我没有看到它在该方法中声明,并且您的命名(下划线前缀,您是从其他语言导入该约定吗?)表明它“存在”于该方法之外。也许其他代码在执行期间正在改变它?解决后请告诉我们;悬念正在杀死我。

于 2009-07-09T13:59:18.623 回答
1

为避免出现问题,您的方法equals()hashCode()方法应保持一致并符合要求(如其他地方所述)。

此外,hashCode() 不应依赖于可变成员,否则您计算的哈希码可能会改变,这会影响HashMap. 这将表明自己无法从Hash*集合中检索东西。

于 2009-07-09T14:05:44.887 回答
1

看一下 HashMap 实现的源代码。get 和 containsKey 都使用 key 对象的 hasCode() 和 equals() 方法。

唯一真正的区别,正如所指出的,这是一个微不足道的空检查,是在比较中:

得到:

((k = e.key) == key || key.equals(k))

包含键:

((k = e.key) == key || (key != null && key.equals(k)))

其中 e 是 HashMap 的 Entry 类型。

因此,如果您没有强大的 hashCode() 和/或 equals() 实现,您将遇到问题。此外,如果您的键发生了变异(我看到您没有将类字段声明为 final),您可能会遇到问题。

举个例子:

public class HashMapTest {
    static class KeyCheck {
        int value;
        public KeyCheck(int value) { this.value = value; }
        public void setValue(int value) { this.value = value; }
        @Override public int hashCode() { return value; }
        @Override public boolean equals(Object o) {
            return ((KeyCheck)o).value == this.value;
        }
    }

    public static void main(String args[]) {
        HashMap<KeyCheck, String> map = new HashMap<KeyCheck, String>();
        KeyCheck k1 = new KeyCheck(5);
        KeyCheck k2 = new KeyCheck(5);

        map.put(k1, "Success");

        System.out.println("Key: " + k1 + " Get: " + map.get(k1) +
                           " Contains: " + map.containsKey(k1));
        System.out.println("Key: " + k2 + " Get: " + map.get(k2) +
                           " Contains: " + map.containsKey(k2));

        k1.setValue(10);

        System.out.println("Key: " + k1 + " Get: " + map.get(k1) +
                           " Contains: " + map.containsKey(k1));
        System.out.println("Key: " + k2 + " Get: " + map.get(k2) +
                           " Contains: " + map.containsKey(k2));
    }
}

这将打印出:

键:HashMapTest$KeyCheck@5 获取:成功包含:true
键:HashMapTest$KeyCheck@5 获取:成功包含:true
键:HashMapTest$KeyCheck@a 获取:null 包含:false
键:HashMapTest$KeyCheck@5 获取:null 包含: 错误的

如您所见,在这种情况下,可变性导致 hashCode() 发生变化,这破坏了一切。

于 2009-07-09T16:00:54.417 回答
0

我认为有时您需要哈希码,有时则不需要,所以我认为通过这种方式,您可以在想要购买时关闭哈希码检查,将您想要的所有对象的哈希码更改为 0

public class sample(){
    @JsonIgnore
    private int hashCode = super.hashCode();

    public void setHashCode(int hashCode){
        this.hashCode = hashCode;
    }    

    @Override
    public int hashCode(){
        return this.hashCode;
    }    

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final ReflectObject other = (ReflectObject) obj;
        if (this.hashCode != other.hashCode) {
            return false;
        }
        return true;
    }
}
于 2012-11-28T15:45:04.160 回答