Java HashMap可以工作,但containsKey没有

我试图在HashMap中找到一个键。 我可以使用’get’打印所选键,但是当我在if语句中使用’containsKey’时,找不到它。

我知道密钥存在于Map中,但它一直返回false。 人们有什么想法?

我的代码:

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; } } 

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

正如这里所描述的那样,你必须覆盖equals(Object)方法。

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

containsKey(Object)计算散列键并获取散列指向的对象。 HashMap中的对象将与您放入的Object进行比较。对于这些比较,使用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); } 

来自等于的javadoc

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

请注意,通常需要在重写此方法时覆盖hashCode方法,以便维护hashCode方法的常规协定,该方法声明相等的对象必须具有相等的哈希代码。

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

如果你是,你可以发布它们吗?

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

我唯一能想到的就是这会导致supportLocation的状态以某种方式在get(...)调用和containsKey(...)之间发生变异。

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

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

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

 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,或者更糟糕的是,这些方法完全关闭),并将导致您观察到的行为。

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

另外,hashCode()不应该依赖于可变成员,否则你计算的哈希代码可能会改变,这会影响HashMap的内部工作。 这将揭示自己无法从Hash*集合中检索内容。

在HashMap实现的源代码中占据一席之地。 get和containsKey都使用密钥对象的hasCode()和equals()方法。

唯一真正的区别,正如所指出的,这是一个简单的空检查,在比较中:

得到:

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

containsKey方法:

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

其中e是HashMap的Entry类型。

所以,如果你没有hashCode()和/或equals()的强大实现,你就会遇到问题。 此外,如果您的密钥发生了变异(我发现您没有声明最终的类字段),您可能会遇到问题。

采用以下示例:

 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 map = new HashMap(); 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 Get:Success包含:true
密钥:HashMapTest $ KeyCheck @ 5 Get:Success包含:true
键:HashMapTest $ KeyCheck @ a Get:null包含:false
密钥:HashMapTest $ KeyCheck @ 5 Get:null包含:false

正如您所看到的,在这种情况下,可变性导致hashCode()发生更改,从而破坏了所有内容。

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

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

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

我想有时你需要哈希码,有时候不是这样我认为你可以通过这种方式转动哈希码检查,当你想要购买更改你想要的所有对象的哈希码时

 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; } }