这是一个关于如何实现equals方法的问题,当我需要在List中找到对象的实例时给定一个值,该值是我的成员中的一个实例。
我有一个我实现了等于的对象:
class User {
private String id;
public User(id) {
this.id = id;
}
public boolean equals(Object obj) {
if (!(obj instanceof User)) {
return false;
}
return ((User)obj).id.equals(this.id);
}
}
现在,如果我想在列表中找到一些东西,我会做这样的事情:
public function userExists(String id) {
List<Users> users = getAllUsers();
return users.contains(new User(id));
}
但也许这可能是一个更好的实施?
class User {
private String id;
public boolean equals(Object obj) {
if (!(obj instanceof User)) {
return false;
}
if (obj instanceof String) {
return ((String)obj).equals(this.id);
}
return ((User)obj).id.equals(this.id);
}
}
改为:
public function userExists(String id) {
List<Users> users = getAllUsers();
return users.contains(id);
}
答案 0 :(得分:7)
以第二种方式执行此操作很危险,因为它会破坏相等的对称属性。
Java期望equals()
的实现具有自反性,对称性和传递性。第二个实现会破坏对称性:如果您将User
与表示其ID的String
进行比较,则会得到true
,但如果您将字符串与用户进行比较,则会得到{{1} }}
答案 1 :(得分:6)
对于数学上不相等的事物,不要重写等于。
您可能认为做
是个好主意User bob = new User("Bob");
if (bob.equals("Bob")) {
...
}
但很少。当Strings
与Users
“相等”时,您是否希望所有等于观察代码的人都感到困惑?
如果你想要一个查找方法,写一下
class User {
private String id;
public boolean equals(Object obj) {
if (obj instanceof User) {
User other = (User)obj;
if (id.equals(other.id)) {
return true;
}
}
return false;
}
public String getId() {
return id;
}
}
然后在其他地方的代码维护“快速查找”表。
Map<String, User> idTable = new HashMap<String, User>();
User bob = new User("Bob");
idTable.put(bob.getId(), bob);
public User findUser(String id) {
return idTable.get(id);
}
请注意,这并不会影响equals实现,所以现在您可以安全地Sets
Users
,Lists
Users
等等。令人担心,如果某种形式的字符串会污染作品。
现在,如果找不到合适的地方来维护Map
Users
id
的{{1}}索引,那么您可以随时使用较慢的Iterator
解决方案
List<User> users = new List<User>();
users.add(new User("Bob"));
users.add(new User("Steve"));
users.ass(new User("Ann"));
public User findUser(String id) {
Iterator<User> index = users.iterator();
while (index.hasNext()) {
User user = index.next();
if (id.equals(user.getId())) {
return user;
}
}
return null;
}
答案 2 :(得分:2)
首先,您的第二个实现在功能上与第一个实现等效,因为String
的实例不是User
的实例,并且第一个return
语句将在此之前使其短路检查String
是否发生。
我的意思是,
public boolean equals(Object obj) {
if (!(obj instanceof User)) { // This will execute if obj is a String
return false;
}
if (obj instanceof String) {
// Never executes, because if obj is a String, we already
// returned false above
return ((String)obj).equals(this.id);
}
return ((User)obj).id.equals(this.id);
}
因此,对于答案的其余部分,我将假设其含义是
public boolean equals(Object obj) {
if ( obj == null ) return false; // Add a null check for good measure.
if (!(obj instanceof User)) {
if (obj instanceof String) {
// Now we're checking for a String only if it isn't a User.
return ((String)obj).equals(this.id);
}
return false;
}
return ((User)obj).id.equals(this.id);
}
现在我们来解决实际问题。
实施equals
返回true
进行User
到String
比较是不好的做法,因为equals
应该是对称的(a.equals(b)
当且仅当b.equals(a)
时)。由于String
永远不能等于User
,因此User
永远不应等于String
。
答案 3 :(得分:1)
如果覆盖Object.equals(),请不要忘记覆盖Object.hashCode()。
如果您不遵守合同,则等对象必须具有相同的哈希码,并且您可能会遇到带有集合的probs。
答案 4 :(得分:0)
第二选择非常糟糕。这意味着您允许用户将User
以外的内容传递到User.equals()
(即真正Object.equals()
,当您 与用户。所以你实质上违反了equals
应该做的合同。
此外,你的答案都没有处理空值检查。