Java 首选的Equals()方法实现
这是一个关于如何实现equals方法的问题,当我需要在给定一个值的列表中查找对象的实例时,我的一个实例在其成员中有一个值 我有一个对象,其中我实现了equals:Java 首选的Equals()方法实现,java,Java,这是一个关于如何实现equals方法的问题,当我需要在给定一个值的列表中查找对象的实例时,我的一个实例在其成员中有一个值 我有一个对象,其中我实现了equals: class User { private String id; public User(id) { this.id = id; } public boolean equals(Object obj) { if (!(obj instanceof User)) {
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));
}
换言之:
public function userExists(String id) {
List<Users> users = getAllUsers();
return users.contains(id);
}
公共函数userExists(字符串id){
列表用户=getAllUsers();
返回users.contains(id);
}
第二种方法是危险的,因为它破坏了等式的对称性
Java希望
equals()
的实现是自反的、对称的和可传递的。第二个实现打破了对称性:如果将用户
与表示其ID的字符串
进行比较,则会得到真
,但如果将该字符串与用户进行比较,则首先会得到假
,第二个实现在功能上等同于第一个实现,因为String
的实例不是User
的实例,第一个return
语句将在检查字符串之前将其短路
我的意思是
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);
}
现在我们来谈谈实际问题
为用户
返回真
到-字符串
比较的等于
是不好的做法,因为等于
应该是对称的(a.equals(b)
当且仅当b.equals(a)
)。由于字符串
永远不可能等于用户
,因此用户
永远不应该等于字符串
对于数学上不相等的事物,不要覆盖equals
你可能认为这是一个好主意
User bob = new User("Bob");
if (bob.equals("Bob")) {
...
}
但事实很少如此。当字符串
与用户
相等时,是否希望所有观察代码的相等者都感到困惑
如果需要查找方法,请编写它
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;
}
}
然后将代码放在别处以维护“快速查找”表
第二选择很糟糕。这意味着您允许用户将user
以外的内容传递给user.equals()
(即,当您不是真正尝试与用户比较字符串时,实际上是Object.equals()
),因此您实际上违反了equals
应该做的事情的约定
另外,两个答案都不处理空检查。如果覆盖Object.equals(),请不要忘记覆盖Object.hashCode()
相等的对象必须具有相等的哈希代码,如果不遵守约定,您可能会在集合中遇到问题。实际上,我遇到了一堆代码,它们具有“相等”的idom与所有错误步骤一样,一开始并不太糟糕,但随着集合
开始被大量使用,大量的变通代码被打包在一起,我认为第二种技术是一个bug而不是一个好处。我记得在Sierra/Bates中读到过关于对称的内容,但我想当时我只是浏览了一下。现在我知道得更多。但是,是吗创建虚拟用户对象以在列表中查找用户,给定ID作为字符串,是否正确?@udeleng否,创建虚拟用户以在列表中查找用户
。使用迭代器
搜索列表,检查每个元素上的字段,或者将用户存储在按ID索引的映射中(如下例所示)user.equals(Object)的正确实现
在列表中搜索之前,需要您已经找到了所需的用户。谢谢,我发现了我在短路方面的错误。我只是在发布时在这里键入了代码。我现在了解了对称原则。请看一下我回答@dasblinkenlight的问题。@trutheality老实说,我完全没有领会您的意思。我抱歉,我的假设是他写的第二个equals实现是半正确的,而不是一个概念错误上的错误实际上可以挽救这一天。我想我最好放慢阅读速度,或者喝杯咖啡。我大体上同意你的答案,但OP是覆盖而不是超载的
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);
}
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;
}