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" 是一个字符串。
顺便说一句,您没有覆盖 Object
的 equals
,它需要一个 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
不是 String
,String.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;
}
}
尝试实现陷入奇怪问题的简单任务:
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" 是一个字符串。
顺便说一句,您没有覆盖 Object
的 equals
,它需要一个 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
不是 String
,String.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;
}
}