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类中,确保覆盖hashCode和equals方法。
如果你是,你可以发布它们吗?
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; } }