【发布时间】:2014-04-19 18:32:43
【问题描述】:
我有一个自定义类 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);
}
我处于调试模式时的屏幕截图显示我的表包含两个具有不同值的键,但键本身是相同的(但在 HashMap 中它被认为是不同的)。
【问题讨论】:
-
你有没有机会改变
stuck在添加元素到地图?这肯定会解释它。 -
@JonSkeet 简短回答:是的。长答案:我有另一个类作为变量跟踪当前状态。每个时间步都会从 MarioState 调用更新方法,并在状态发生变化时对其进行修改。然后调用 getActionsQValues 来检查修改后的状态和 HashMap 中的状态。只有当状态对 HashMap 来说是全新的(即 HashMap 中尚未出现的唯一属性组合)时,才应该输入它。
-
简短回答:你已经打破了 HashMap 假设,即在你添加它之后,键的哈希码不会改变 :) 有一个带有相等键的映射是非常奇怪的老实说,仅基于单个
boolean值。 -
@JonSkeet 你是对的。我通过让 update() 方法返回一个新的 MarioState 来稍微更改我的代码,这似乎可以解决它。非常感谢你!至于相等性,它实际上不仅基于该布尔值。这只是状态的一个属性,但状态可以拥有更多的属性(我只发布了我实际使用的状态的简化版本)。使用完整的 MarioState,我有多个具有离散值的属性,这些属性可以导致数百个独特的组合(所有都将存储在 HashMap 中)。然后平等基于所有属性。
-
@JonSkeet 如果您从您的评论中做出回答,我会接受。感谢您的帮助!
标签: java hashmap key equals hashcode