3

这是一个关于当我需要在 List 中查找对象的实例时如何实现 equals 方法的问题,给定一个我在其成员中拥有的实例的值。

我有一个实现等于的对象:

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);
    }
}

现在,如果我想在 List 中找到一些东西,我会这样做:

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);
}
4

5 回答 5

7

第二种方式是危险的,因为它破坏了等式的对称性

Java 期望 的实现equals()是自反的、对称的和传递的。第二种实现打破了对称性:如果您将User其与String表示它的 ID 进行比较,您将得到true,但如果将字符串与用户进行比较,您将获得false.

于 2012-04-04T19:15:54.557 回答
6

不要为数学上不相等的事物覆盖等于。

你可能认为这样做是个好主意

User bob = new User("Bob");
if (bob.equals("Bob")) {
  ...
}

但它很少。您是否希望所有 equals 观察代码在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 实现混淆,因此现在您可以安全地拥有Setsof UsersListsofUsers等,而不必担心 String 是否会以某种方式破坏作品。

现在如果你找不到一个好的地方来维护一个MapUsers他们索引的id,你总是可以使用较慢的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;
}
于 2012-04-04T19:17:08.823 回答
2

首先,您的第二个实现在功能上与第一个实现等效,因为 的实例String不是 的实例User,并且第一条语句将在检查 a发生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))。因为 aString永远不能等于 a User,所以 a 永远User不应该等于 a String

于 2012-04-04T19:16:35.877 回答
1

如果您覆盖 Object.equals(),请不要忘记覆盖 Object.hashCode()。

相等的对象必须具有相等的哈希码,如果您不遵守合同,您可能会遇到 Collections 的问题。

于 2012-04-04T19:41:52.930 回答
0

第二个选择非常糟糕。这意味着您允许用户传递除 a 之外的其他内容UserUser.equals()即,实际上Object.equals(),当您并没有真正尝试将字符串与用户进行比较时。因此,您实质上违反了equals应做的合同。

此外,您的答案都没有处理空值检查。

于 2012-04-04T19:19:17.630 回答