java - HashMap 的 ContainsKey 不适用于自定义类作为键

标签 java hashmap key equals hashcode

我有一个自定义类 MarioState,我想在 HashMap 中使用它。该类代表马里奥游戏的状态空间中的可能状态。下面是 MarioState 类的简化版本。

在我的 HashMap 中,我想存储这些状态。然而,在比较两个 MarioState 时,并不需要考虑 MarioState 中的属性。例如,如果一个 MarioState 将卡住属性设置为 true 且距离为 30,而另一个 MarioState 也将卡住属性设置为 true 但距离值不同(例如 20),那么它们仍然应被视为相同。

我知道要在我的 HashMap 中工作,我必须实现 .equals() 和 .hashcode() 方法,这就是我所做的(让它们由 InteliJ IDE 自动生成)。

public class MarioState{

    // Tracking the distance Mario has moved.
    private int distance;
    private int lastDistance;

    // To keep track of if Mario is stuck or not.
    private int stuckCount;
    private boolean stuck;

    public MarioState(){
        stuckCount = 0;
        stuck = false;

        distance = 0;
        lastDistance = 0;
    }

    public void update(Environment environment){

        // Computing the distance
        int tempDistance = environment.getEvaluationInfo().distancePassedPhys;
        distance = tempDistance - lastDistance;
        lastDistance = tempDistance;

        // If Mario hasn't moved for over 25 turns then this means he is stuck.
        if(distance == 0){
            stuckCount++;
        } else {
            stuckCount = 0;
            stuck = false;
        }

        if(stuckCount > 25){ stuck = true; }
    }

    public float calculateReward(){
        float reward = 0f;
        reward += distance * 2;
        if(stuck){ reward += -20; }
        return reward;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        MarioState that = (MarioState) o;

        if (stuck != that.stuck) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return (stuck ? 1 : 0);
    }  
}
然而,问题是,在运行代码时,某些键被认为是不同的,而它们不应该根据它们的 .equals() 和 .hashcode() 函数。什么可能导致这种情况?我是不是忘记了什么?

在HashMap中插入状态时使用的代码(如果需要可以提供附加信息):

public float[] getActionsQValues(MarioState state){
    if(!table.containsKey(state)) {
        float[] initialQvalues = getInitialQvalues(state);
        table.put(state, initialQvalues);
        return initialQvalues;
    }
    return table.get(state);
}

我处于 Debug模式时的屏幕截图显示我的表包含两个具有不同值的键,但键本身是相同的(但在 HashMap 中它被认为是不同的)。

Screenshot of Debug Mode

最佳答案

您的哈希码计算和相等性比较都基于卡住 - 但这可能会随着时间的推移而改变。

如果您在将对象添加为 HashMap 中的键后对其进行变异,从而使哈希码发生变化,那么当您稍后请求它时将找不到该键 - 因为存储的哈希码第一次添加的 key 将不再与其当前的哈希码相同。

尽可能避免使用可变对象作为映射中的键(即使是不使用哈希码的 TreeMap ,如果您以以下方式更改对象,也会出现相同的问题:会改变相对顺序)。如果您必须使用可变对象作为映射中的键,则应避免在将它们添加为键后更改它们。

关于java - HashMap 的 ContainsKey 不适用于自定义类作为键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23173876/

相关文章:

java - 子类可以与父类(super class)具有相同的列名吗?

java - (filename.jar) 和 (java -jar filename.jar) 之间的区别

PHP/MySQL : How to store Select results in array

java - HashMap值自增

java - 方法 getOrDefault(String, String) 未定义类型 Map<String,String>

cocoa - 不复制 iPhone 3.0 SDK key 的 NSDictionary?

sql - Oracle SQL - 将主键添加到表

java - scribe 在 oauth 2.0 中不支持 refresh_token 对吗?

java - JDK : how to enable PlatformLogger programmatically

java - 使用 hashmap 读取 xls 文件