Java HashMap containsKey 奇怪的行为

Java HashMap containsKey strange behavior

尝试实现陷入奇怪问题的简单任务:

class User{
    String login;
    String pwrd;
    User(String lg,String pw){
        this.login=lg;
        this.pwrd=pw;
    }
    public String toString(){
        return this.login;
    }
    public boolean equals(String a){
        return this.login.equals(a);
    }
    public boolean equals(User t){
        return this.login.equals(t.toString());
    }
}

public class Foo{
    public static void main (String[] args)
    {
        HashMap<User,Boolean> a=new HashMap<>();
        User a1=new User("asd","123"),a2=new User("asd","134");
        a.put(a1,false);
        a.put(a2,false);
        System.out.println(a.containsKey(a2));
        System.out.println(a.containsKey("asd"));
    }
}

因此,我预计 containsKey 两项检查都是正确的。在代码中,它会被越来越多地使用。因此,首先要了解它为什么会这样,并在可能的情况下修复它。任何帮助表示赞赏。

您的地图的键是 User 个实例,因此 a.containsKey("asd") 永远不会 return true,因为 "asd" 是一个字符串。

顺便说一句,您没有覆盖 Objectequals,它需要一个 Object 参数。这意味着 a.containsKey(a2) 也 return 是 false,因为 a1==a2 是假的。

equals 的正确实现应该是:

@Override
public boolean equals(Object other){
    if (!(other instanceof User))
        return false;
    User u = (User) other;
    return this.login.equals(u.login);
}

正如 Andy 提到的,您还必须覆盖 hashCode,这样如果 a.equals(b) 为真,则 a.hashCode()==b.hashCode().

编辑:

我认为如果您以一种将 String 实例视为等于 User 实例的方式重写 equals,我认为您可以使 a.containsKey("asd") return 为真如果它们匹配 login 属性 :

@Override
public boolean equals(Object other){
    if (other instanceof User) {
        User u = (User) other;
        return this.login.equals(u.login);
    } else if (other instanceof String) {
        String u = (String) other;
        return this.login.equals(u);
    }
    return false;
}

@Override
public int hashCode()
{
    return login.hashCode();
}

我从未尝试过 equals 的这种实现,但根据我对 HashMap 的理解,它可能会起作用。

但是,equals 的这种实现将违反 Object 的 Javadoc 中定义的 equals 的约定,因为 "asd".equals(a1) 将 return即使 a1.equals("asd") 为真,也为假。

编辑:

检查 HashMap 的实现后,我发现 equals 的这个实现不起作用,因为 containsKey(key) 的代码将键与现有条目的键进行比较相反,如果 obj 不是 StringString.equals(obj) 将始终 return false。我想有充分的理由不破坏 equals.

的契约

您需要覆盖 public boolean equals(Object other) 并在该方法内部检查传递的其他对象的类型是否正确。请注意,传递的对象 也可能为 null

public class User {
  public boolean equals(Object other) {
    //Is the same
    if(other == this) {
      return true;
    }
    //Other is a user as well - Includes null-check (thanks, Kevin!)
    if(other instanceof User) {
      //equal if usernames are equal
      return login.equals(other.login);
    }
    //anything else - not equal / null, whatever
    return false;
  }
}