首选Equals()方法实现

时间:2012-04-04 19:10:03

标签: java

这是一个关于如何实现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);
}

5 个答案:

答案 0 :(得分:7)

以第二种方式执行此操作很危险,因为它会破坏相等的对称属性。

Java期望equals()的实现具有自反性,对称性和传递性。第二个实现会破坏对称性:如果您将User与表示其ID的String进行比较,则会得到true,但如果您将字符串与用户进行比较,则会得到{{1} }}

答案 1 :(得分:6)

对于数学上不相等的事物,不要重写等于。

您可能认为做

是个好主意
User bob = new User("Bob");
if (bob.equals("Bob")) {
  ...
}

但很少。当StringsUsers“相等”时,您是否希望所有等于观察代码的人都感到困惑?

如果你想要一个查找方法,写一下

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 UsersLists 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进行UserString比较是不好的做法,因为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应该做的合同。

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