每次创建对象时 hashCode 都会改变
hashCode changes each time the object is created
关于 hashCode,我有些不解。我知道如果我重写
等于,那么我也必须重写 hashCode。我也知道如果两个对象是
相等,则两个对象的hashCode一定相等。如果在 equals 方法中检查的字段没有改变,那么 hashCode 应该不会改变,对吧?
如果是这样,那么我不明白为什么每次创建下面对象的实例时,我都会得到不同的 hashCode:
public class Effect {
private long timestamp;
private int damage;
private SquareType squareType;
public Effect(long timestamp, int damage, SquareType squareType) {
this.timestamp = timestamp;
this.damage = damage;
this.squareType = squareType;
}
public long getTimestamp() {
return timestamp;
}
public int getDamage() {
return damage;
}
public SquareType getSquareType() {
return squareType;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Effect effect = (Effect) o;
if (getDamage() != effect.getDamage()) return false;
return getSquareType() == effect.getSquareType();
}
@Override
public int hashCode() {
int result = getDamage();
result = 31 * result + (getSquareType() != null ? getSquareType().hashCode() : 0);
return result;
}
@Override
public String toString() {
String ret = "Effect hashcode: " + hashCode();
return ret;
}
}
在代码中,我随着时间的推移不断创建这种对象。每次更改的唯一字段是 "timestamp",但其他两个字段不会更改(除非有特定事件)。发生的情况是 hashCode 值总是不同的,即使 "damage" 和 "SquareType" 相同。
我没有在我的 equals 和 hashCode 中使用 "timestamp",所以我不明白为什么我会出现这种行为。
更新
这是 SquareType:
public enum SquareType {
FIRE, WIND, WATER, EARTH
}
更新 2
例如,如果我创建了 10 个 Effect 实例,我将遍历它们并且我
打印它们(toString() returns hashCode 值)我得到 10 个不同的值。
如果 Effect 的两个实例具有相同的 "damage" 和 "SquareType" 那么它们必须相等并且具有相同的 hashCode。
更新 3
效果是这样创建的:
@Override
public void friendlyFire(BaseBullet bullet, BaseSquare square) {
square.notifyFriendlyFire(new Effect(TimeUtils.millis(),
square.getDamage(), square.getSquareType()), new MyKey(square.getUniqueID()));
}
唯一改变的 Effect 字段是时间戳,我不在 equals 和 hashCode 中使用它。
public void notifyFriendlyFire(Effect newEffect, MyKey key) {
// System.out.println("The map contains the key? " + effectMap.containsKey(key));
if(effectMap.containsKey(key)) {
Effect oldEffect = effectMap.get(key);
System.out.println(newEffect);
if(!oldEffect.equals(newEffect)) {
System.out.println("old effect changed!");
// remove the old effect
removeEffect(oldEffect);
// update the map with the new effect
effectMap.put(key, newEffect); //
// apply the new effect
applyEffect(newEffect);
}
}
else {
// new effect
effectMap.put(key, newEffect);
applyEffect(newEffect);
}
}
检查 "if(!oldEffect.equals(newEffect))" 始终为真,即使损坏和类型相同。
更新 4
我找到了这个错误。伤害不断增加。现在我只是想弄清楚为什么...
这是我对您的实施的近似尝试:
package cruft;
import java.util.Date;
/**
* Equals and hashCode test
* Creation date 1/16/2016.
* @link
*/
public class OverrideDemo {
private long timestamp;
private int damage;
private SquareType squareType;
public OverrideDemo(int damage, SquareType squareType) {
this(damage, squareType, new Date().getTime());
}
public OverrideDemo(int damage, SquareType squareType, long timestamp) {
if (squareType == null) throw new IllegalArgumentException("square type cannot be null");
this.timestamp = timestamp;
this.damage = damage;
this.squareType = squareType;
}
public long getTimestamp() {
return timestamp;
}
public int getDamage() {
return damage;
}
public SquareType getSquareType() {
return squareType;
}
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
OverrideDemo that = (OverrideDemo) o;
if (damage != that.damage) { return false; }
return squareType == that.squareType;
}
@Override
public int hashCode() {
int result = damage;
result = 31 * result + squareType.hashCode();
return result;
}
@Override
public String toString() {
return "OverrideDemo{" +
"timestamp=" + timestamp +
", damage=" + damage +
", squareType=" + squareType +
'}';
}
}
enum SquareType { FIRE, WIND, WATER, EARTH }
这是一个 Junit 测试,显示了这些方法的行为方式。所有测试通过;我认为这个实现是正确的。
package cruft;
import org.junit.Assert;
import org.junit.Test;
/**
* Junit test demonstrates testing equals and hashcode contract
* Created by Michael
* Creation date 1/16/2016.
* @link
*/
public class OverrideDemoTest {
@Test
public void testEquals_Null() {
OverrideDemo x = new OverrideDemo(5, SquareType.EARTH);
Assert.assertFalse(x.equals(null));
}
@Test
public void testEquals_Reflexive() {
OverrideDemo x = new OverrideDemo(5, SquareType.EARTH);
Assert.assertTrue(x.equals(x));
}
@Test
public void testEquals_Symmetric() {
OverrideDemo x = new OverrideDemo(5, SquareType.EARTH);
OverrideDemo y = new OverrideDemo(5, SquareType.EARTH);
Assert.assertTrue(x.equals(y));
Assert.assertTrue(y.equals(x));
Assert.assertTrue(x.hashCode() == y.hashCode());
}
@Test
public void testEquals_Transitive() {
OverrideDemo x = new OverrideDemo(5, SquareType.EARTH);
OverrideDemo y = new OverrideDemo(5, SquareType.EARTH);
OverrideDemo z = new OverrideDemo(5, SquareType.EARTH);
Assert.assertTrue(x.equals(y));
Assert.assertTrue(y.equals(z));
Assert.assertTrue(z.equals(x));
Assert.assertTrue(x.hashCode() == y.hashCode());
Assert.assertTrue(y.hashCode() == z.hashCode());
Assert.assertTrue(z.hashCode() == x.hashCode());
}
@Test
public void testEquals_DifferentDamage_NotEqual() {
OverrideDemo x = new OverrideDemo(5, SquareType.EARTH);
OverrideDemo y = new OverrideDemo(10, SquareType.EARTH);
Assert.assertFalse(x.equals(y));
Assert.assertFalse(y.equals(x));
Assert.assertFalse(x.hashCode() == y.hashCode());
}
@Test
public void testEquals_DifferentSquareType_NotEqual() {
OverrideDemo x = new OverrideDemo(10, SquareType.EARTH);
OverrideDemo y = new OverrideDemo(10, SquareType.FIRE);
Assert.assertFalse(x.equals(y));
Assert.assertFalse(y.equals(x));
Assert.assertFalse(x.hashCode() == y.hashCode());
}
}
关于 hashCode,我有些不解。我知道如果我重写 等于,那么我也必须重写 hashCode。我也知道如果两个对象是 相等,则两个对象的hashCode一定相等。如果在 equals 方法中检查的字段没有改变,那么 hashCode 应该不会改变,对吧?
如果是这样,那么我不明白为什么每次创建下面对象的实例时,我都会得到不同的 hashCode:
public class Effect {
private long timestamp;
private int damage;
private SquareType squareType;
public Effect(long timestamp, int damage, SquareType squareType) {
this.timestamp = timestamp;
this.damage = damage;
this.squareType = squareType;
}
public long getTimestamp() {
return timestamp;
}
public int getDamage() {
return damage;
}
public SquareType getSquareType() {
return squareType;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Effect effect = (Effect) o;
if (getDamage() != effect.getDamage()) return false;
return getSquareType() == effect.getSquareType();
}
@Override
public int hashCode() {
int result = getDamage();
result = 31 * result + (getSquareType() != null ? getSquareType().hashCode() : 0);
return result;
}
@Override
public String toString() {
String ret = "Effect hashcode: " + hashCode();
return ret;
}
}
在代码中,我随着时间的推移不断创建这种对象。每次更改的唯一字段是 "timestamp",但其他两个字段不会更改(除非有特定事件)。发生的情况是 hashCode 值总是不同的,即使 "damage" 和 "SquareType" 相同。 我没有在我的 equals 和 hashCode 中使用 "timestamp",所以我不明白为什么我会出现这种行为。
更新
这是 SquareType:
public enum SquareType {
FIRE, WIND, WATER, EARTH
}
更新 2
例如,如果我创建了 10 个 Effect 实例,我将遍历它们并且我 打印它们(toString() returns hashCode 值)我得到 10 个不同的值。
如果 Effect 的两个实例具有相同的 "damage" 和 "SquareType" 那么它们必须相等并且具有相同的 hashCode。
更新 3
效果是这样创建的:
@Override
public void friendlyFire(BaseBullet bullet, BaseSquare square) {
square.notifyFriendlyFire(new Effect(TimeUtils.millis(),
square.getDamage(), square.getSquareType()), new MyKey(square.getUniqueID()));
}
唯一改变的 Effect 字段是时间戳,我不在 equals 和 hashCode 中使用它。
public void notifyFriendlyFire(Effect newEffect, MyKey key) {
// System.out.println("The map contains the key? " + effectMap.containsKey(key));
if(effectMap.containsKey(key)) {
Effect oldEffect = effectMap.get(key);
System.out.println(newEffect);
if(!oldEffect.equals(newEffect)) {
System.out.println("old effect changed!");
// remove the old effect
removeEffect(oldEffect);
// update the map with the new effect
effectMap.put(key, newEffect); //
// apply the new effect
applyEffect(newEffect);
}
}
else {
// new effect
effectMap.put(key, newEffect);
applyEffect(newEffect);
}
}
检查 "if(!oldEffect.equals(newEffect))" 始终为真,即使损坏和类型相同。
更新 4
我找到了这个错误。伤害不断增加。现在我只是想弄清楚为什么...
这是我对您的实施的近似尝试:
package cruft;
import java.util.Date;
/**
* Equals and hashCode test
* Creation date 1/16/2016.
* @link
*/
public class OverrideDemo {
private long timestamp;
private int damage;
private SquareType squareType;
public OverrideDemo(int damage, SquareType squareType) {
this(damage, squareType, new Date().getTime());
}
public OverrideDemo(int damage, SquareType squareType, long timestamp) {
if (squareType == null) throw new IllegalArgumentException("square type cannot be null");
this.timestamp = timestamp;
this.damage = damage;
this.squareType = squareType;
}
public long getTimestamp() {
return timestamp;
}
public int getDamage() {
return damage;
}
public SquareType getSquareType() {
return squareType;
}
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
OverrideDemo that = (OverrideDemo) o;
if (damage != that.damage) { return false; }
return squareType == that.squareType;
}
@Override
public int hashCode() {
int result = damage;
result = 31 * result + squareType.hashCode();
return result;
}
@Override
public String toString() {
return "OverrideDemo{" +
"timestamp=" + timestamp +
", damage=" + damage +
", squareType=" + squareType +
'}';
}
}
enum SquareType { FIRE, WIND, WATER, EARTH }
这是一个 Junit 测试,显示了这些方法的行为方式。所有测试通过;我认为这个实现是正确的。
package cruft;
import org.junit.Assert;
import org.junit.Test;
/**
* Junit test demonstrates testing equals and hashcode contract
* Created by Michael
* Creation date 1/16/2016.
* @link
*/
public class OverrideDemoTest {
@Test
public void testEquals_Null() {
OverrideDemo x = new OverrideDemo(5, SquareType.EARTH);
Assert.assertFalse(x.equals(null));
}
@Test
public void testEquals_Reflexive() {
OverrideDemo x = new OverrideDemo(5, SquareType.EARTH);
Assert.assertTrue(x.equals(x));
}
@Test
public void testEquals_Symmetric() {
OverrideDemo x = new OverrideDemo(5, SquareType.EARTH);
OverrideDemo y = new OverrideDemo(5, SquareType.EARTH);
Assert.assertTrue(x.equals(y));
Assert.assertTrue(y.equals(x));
Assert.assertTrue(x.hashCode() == y.hashCode());
}
@Test
public void testEquals_Transitive() {
OverrideDemo x = new OverrideDemo(5, SquareType.EARTH);
OverrideDemo y = new OverrideDemo(5, SquareType.EARTH);
OverrideDemo z = new OverrideDemo(5, SquareType.EARTH);
Assert.assertTrue(x.equals(y));
Assert.assertTrue(y.equals(z));
Assert.assertTrue(z.equals(x));
Assert.assertTrue(x.hashCode() == y.hashCode());
Assert.assertTrue(y.hashCode() == z.hashCode());
Assert.assertTrue(z.hashCode() == x.hashCode());
}
@Test
public void testEquals_DifferentDamage_NotEqual() {
OverrideDemo x = new OverrideDemo(5, SquareType.EARTH);
OverrideDemo y = new OverrideDemo(10, SquareType.EARTH);
Assert.assertFalse(x.equals(y));
Assert.assertFalse(y.equals(x));
Assert.assertFalse(x.hashCode() == y.hashCode());
}
@Test
public void testEquals_DifferentSquareType_NotEqual() {
OverrideDemo x = new OverrideDemo(10, SquareType.EARTH);
OverrideDemo y = new OverrideDemo(10, SquareType.FIRE);
Assert.assertFalse(x.equals(y));
Assert.assertFalse(y.equals(x));
Assert.assertFalse(x.hashCode() == y.hashCode());
}
}