Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/381.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 重写hashCode()和equals()以在hashMap中存储一个工作不正常的对象_Java_Hashmap_Scjp - Fatal编程技术网

Java 重写hashCode()和equals()以在hashMap中存储一个工作不正常的对象

Java 重写hashCode()和equals()以在hashMap中存储一个工作不正常的对象,java,hashmap,scjp,Java,Hashmap,Scjp,我重写了类(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)) {

我重写了类(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();
   }
}
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()); 
  }
}
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();
   }
}
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()); 
  }
}
公共类映射测试{
公共静态void main(字符串[]args){
Map m=新的HashMap();
m、 put(“k1”,新狗(“爱子”);
狗d1=新狗(“三叶草”);
m、 放置(d1,“狗钥匙”);/#1
System.out.println(m.get(“k1”);
字符串k2=“k2”;
d1.name=“亚瑟”/#2
System.out.println(m.get(d1));#3
System.out.println(m.size());
}
}

问题是,在2时,我将存储在hashMap中的dog对象的名称更改为1,在3时的预期输出为NULL,但实际输出为dog Key!!我希望它在equals()方法中失败=亚瑟,但是成功了!!我注意到,当hashCode成功时(即lengh==6),即使equals()方法失败,也会检索存储在映射中的值,我更改了==并使用equals(),但没有发生任何更改,问题仍然存在

您希望使用.equals()而不是==,来比较字符串,后者比较引用

public boolean equals(Object o) {
    if ((o instanceof Dog)
            && (((Dog) o).name.equals(name))) {
       return true;
    } else {
       return false;
    }
}

另外,equals方法有点不合适。如果名称为空怎么办?您将得到一个空指针异常。您需要为该特殊情况添加另一个检查。

除了将字符串与
==
进行比较的问题之外,所发生的是您更改了狗的名称

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中查找,当然,它会在那里找到狗

如果你把名字改成一个不同长度的名字,它(通常)会在不同的桶里找不到狗。

为什么
等于
永不失败?
根据Tom的评论:

。。如果修改贴图中的对象但保持键不变,则仍然可以使用相同的键实例获取值dog.equals(dog)在代码中始终为真(除非有并发修改)

也就是说,行:

d1.name = "arthur"; 
正在对HashMap中已存在的对象进行变异。比较(其中
t
“打印”为真或假):

因此,
等于
永远不会失败,因为它将对象与自身进行比较:)

一开始我也没注意到:记住Java有语义。也就是说,对于传递给方法的对象,没有隐式复制/克隆/复制

那么,如何让它失败:

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
是如何适应的呢? 散列码用于确定要将密钥(和值对)放入的bucket。执行查找(或设置)时,首先通过哈希代码查找存储桶,然后使用
equals
检查已映射到存储桶的每个键。如果bucket中没有键,则永远不会调用equals

这解释了为什么在原始post中将
名称
更改为长度为8的字符串会导致查找失败:最初选择不同的bucket(例如,一个为空的bucket),因此不会对其他bucket中存在的现有密钥调用
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);
可能有许多哈希代码映射到同一个bucket。但是,以上是成功查找的唯一保证



当然,有关比较字符串的正确方法,请参阅其他回复。

我真的希望Java编译器在
x==y
上发出警告/错误,其中x/y的类型不是原语。(顺便说一句,对于各种形式,它确实在C#/VS中发出警告。)也就是说,我真的希望需要一个显式的
(object)x==(object)y
形式(或其他形式)。。这个问题提得很好。不完全遵循你正在做的事情。如果修改贴图中的对象但保持键不变,则仍然可以使用相同的键实例获取值
dog.equals(dog)
在你的代码中总是正确的(除非同时修改)。@TomHawtin-tackline请作为答案发布。。我错过了这一点,其他两张海报也错过了。)@我感到困惑。“你发帖子,我会向上投票”。@TomHawtin tackline在那里,评论为你引用帖子。没有任何变化,它仍然返回Dog键。这似乎与问题无关——首先,所有字符串都被保留。这是一个错误的假设,因为他的源代码只是示例代码。从数据库读入字符串值的那一刻,
=
即被中断
equals
是比较字符串的正确方法。@SteveKuo字符串文字由要插入的语言规范保证。是的,您可以更改源,它可以做一些不同的事情,但问题是关于这个确切源的行为。@SteveKuo字符串文字是。在JLS中应该不太难找到。这不是真的。嗯,是的。但它并不完整。OP解释了长度和铲斗匹配。这仍然不能解释“equals not failing”。当然,它将查看同一个hashBucket,这是第一步,然后equals()方法将在第二步确定这两个是否相等,实际上它们不是t@Eslam但它们是相等的(这就是为什么
equals
总是返回true),请参见Tom在主帖子中的评论。@Eslam
d1
是地图中的键,您查找的对象是同一个对象。如果您的答案是正确的,那么当我传递一个大于6个字符(即d1.name=“Magnolia”)的名称时,为什么返回空值?@Eslam Per没有按照帖子找到正确的桶
hashCode()
用于首先查找bucket,而equals仅用于在bucket中找到的对象。(这就是为什么
hashCode
应该是稳定的。)@Eslam Each
Ha