[英]override hashCode() and equals() to store an object inside hashMap not working properly
我在类(Dog)中重写了hashCode()和equals(),以便从hashMap中存储和检索它的实例,代码如下:
class Dog {
public Dog(String n) {
name = n;
}
public String name;
public boolean equals(Object o) {
if ((o instanceof Dog)
&& (((Dog) o).name == name)) {
return true;
} else {
return false;
}
}
public int hashCode() {
return name.length();
}
}
hashMap代码如下:
public class MapTest {
public static void main(String[] args) {
Map<Object, Object> m = new HashMap<Object, Object>();
m.put("k1", new Dog("aiko"));
Dog d1 = new Dog("clover");
m.put(d1, "Dog key"); // #1
System.out.println(m.get("k1"));
String k2 = "k2";
d1.name = "arthur"; // #2
System.out.println(m.get(d1)); #3
System.out.println(m.size());
}
}
问题是,在2我更改了存储在hashMap内的狗对象的名称为1,3的预期输出为NULL但实际是Dog Key !! 我希望它在equals()方法中失败,因为三叶草!=亚瑟,但它成功了!! 我注意到当hashCode成功(即lengh == 6)时,即使equals()方法失败,也会检索存储在地图中的值,我改变了==并使用了equals()代替但没有发生变化,问题仍然存在。
您想要使用.equals()not ==来比较字符串,它会比较引用。
public boolean equals(Object o) {
if ((o instanceof Dog)
&& (((Dog) o).name.equals(name))) {
return true;
} else {
return false;
}
}
另外,等于方法有点偏。 如果name为null怎么办? 你会得到一个空指针异常。 您需要为该特殊情况添加另一个检查。
equals
“永不失败”? 根据汤姆的评论:
..如果您修改地图中的对象但保持密钥不变,那么您仍然可以使用相同的密钥实例获取值。 dog.equals(dog)将始终与您的代码一致(除非有并发修改)
就是这条线:
d1.name = "arthur";
正在改变 HashMap中的对象。 比较(其中t
“打印”为真或假):
Dog d1 = new Dog("clover");
// what "put" is effectively doing: there is *no* Copy/Clone
Dog inMap = d1;
t(inMap == d1); // true: same object, reference equality!
d1.name = "arthur";
t(inMap.name.equals("arthur")); // true: same object! "name" member was *mutated*
t(d1.equals(inMap)); // true: same object!
所以equals
永远不会失败,因为它是将对象与自身进行比较:)
我最初也错过了它:记住Java具有Call By Object-Sharing语义。 也就是说,传递给方法的对象没有隐式的复制/克隆/复制 。
那么,如何让它失败:
Dog d1 = new Dog("clover");
Dog d2 = new Dog("clover");
t(d1 == d2); // false: different objects!
m.put(d1, "Dog key"); // put in with D1 object
System.out.println(m.get(d1)); // "Dog key" -okay, equals
System.out.println(m.get(d2)); // "Dog key" -okay, equals
d2.name = "arthur"; // *mutate* D2 object
t(d1.equals(d2)); // false: no longer equal
System.out.println(m.get(d1)); // "Dog key" -okay, always equals, as per above
System.out.println(m.get(d2)); // "" -no good, no longer equals
hashCode
如何适应? 哈希码用于确定要放入密钥(和值对) 的哈希表桶 。当执行查找(或设置)时, 首先 通过哈希码查找存储桶,然后每个密钥已映射到存储桶用equals
检查。 如果存储桶中没有密钥,则永远不会调用equals。
这解释了为什么在原始帖子中将name
更改为长度为8的字符串会导致查找失败:最初选择了一个不同的存储桶(例如,一个为空的存储桶),因此永远不会在存在于其中的现有密钥上调用equals
其他水桶 。 可能已存在相同的对象键,但它从未被查看过!
那么,如何使用不同的哈希代码使其失败:
Dog d1 = new Dog("clover");
m.put(d1, "Dog key"); // put in with D1 object, hashCode = 6
System.out.println(m.get(d1)); // "Dog key" -okay, hashCode = 6, equals
d1.name = "Magnolia"; // change value such that it changes hash code
System.out.println(m.get(d1)); // "" -fail, hashCode = 8, equals
// ^-- attaching a debugger will show d1.equals is not called
因此,对于在哈希表(例如HashMap)中找到的键,它必须是这样的:
k.hashCode() == inMap.hashCode() && k.equals(inMap);
可能有许多哈希码映射到同一个桶。 然而上述的唯一保证查找会成功。
当然,请参阅其他回复,了解一般比较字符串的正确方法。
除了将字符串与==
进行比较的问题之外,发生的事情是您更改了狗的名字
Dog d1 = new Dog("clover");
m.put(d1, "Dog key"); // #1
System.out.println(m.get("k1"));
String k2 = "k2";
d1.name = "arthur"; // #2
到一个长度相同的人。 因此,地图中的查找看起来在同一个hashbucket中,当然它在那里找到了狗。
如果您将名称更改为具有不同长度的名称,它将(通常)查看不同的桶,然后找不到狗。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.