在大多数情况下覆盖 equals() 的主要原因是检查某些集合中的重复项。例如,如果您想使用 Set 来包含您创建的对象,则需要在您的对象中覆盖 equals() 和 hashCode()。如果您想将自定义对象用作 Map 中的键,这同样适用。
这一点很重要,因为我看到很多人在实践中犯了一个错误,即在没有覆盖 equals() 和 hashCode() 的情况下将自定义对象添加到 Sets 或 Maps。这可能特别阴险的原因是编译器不会抱怨,您最终可能会得到多个对象,这些对象包含相同的数据,但在不允许重复的 Collection 中具有不同的引用。
例如,如果您有一个名为 NameBean 的简单 bean,它有一个字符串属性“name”,您可以构造两个 NameBean 实例(例如 name1 和 name2),每个实例都具有相同的“name”属性值(例如“Alice”)。然后,您可以将 name1 和 name2 添加到 Set 中,并且该集合的大小为 2,而不是预期的大小 1。同样,如果您有一个 Map (例如 Map)以便将名称 bean 映射到其他对象,并且您首先将 name1 映射到字符串“first”,然后将 name2 映射到字符串“second”,那么您将拥有两个键/值对在地图中(例如 name1->"first", name2->"second")。因此,当您进行地图查找时,它将返回映射到您传入的确切引用的值,即 name1、name2 或另一个名为“的引用”
这是一个具体示例,前面是运行它的输出:
输出:
Adding duplicates to a map (bad):
Result of map.get(bean1):first
Result of map.get(bean2):second
Result of map.get(new NameBean("Alice"): null
Adding duplicates to a map (good):
Result of map.get(bean1):second
Result of map.get(bean2):second
Result of map.get(new ImprovedNameBean("Alice"): second
代码:
// This bean cannot safely be used as a key in a Map
public class NameBean {
private String name;
public NameBean() {
}
public NameBean(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
// This bean can safely be used as a key in a Map
public class ImprovedNameBean extends NameBean {
public ImprovedNameBean(String name) {
super(name);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if(obj == null || getClass() != obj.getClass()) {
return false;
}
return this.getName().equals(((ImprovedNameBean)obj).getName());
}
@Override
public int hashCode() {
return getName().hashCode();
}
}
public class MapDuplicateTest {
public static void main(String[] args) {
MapDuplicateTest test = new MapDuplicateTest();
System.out.println("Adding duplicates to a map (bad):");
test.withDuplicates();
System.out.println("\nAdding duplicates to a map (good):");
test.withoutDuplicates();
}
public void withDuplicates() {
NameBean bean1 = new NameBean("Alice");
NameBean bean2 = new NameBean("Alice");
java.util.Map<NameBean, String> map
= new java.util.HashMap<NameBean, String>();
map.put(bean1, "first");
map.put(bean2, "second");
System.out.println("Result of map.get(bean1):"+map.get(bean1));
System.out.println("Result of map.get(bean2):"+map.get(bean2));
System.out.println("Result of map.get(new NameBean(\"Alice\"): "
+ map.get(new NameBean("Alice")));
}
public void withoutDuplicates() {
ImprovedNameBean bean1 = new ImprovedNameBean("Alice");
ImprovedNameBean bean2 = new ImprovedNameBean("Alice");
java.util.Map<ImprovedNameBean, String> map
= new java.util.HashMap<ImprovedNameBean, String>();
map.put(bean1, "first");
map.put(bean2, "second");
System.out.println("Result of map.get(bean1):"+map.get(bean1));
System.out.println("Result of map.get(bean2):"+map.get(bean2));
System.out.println("Result of map.get(new ImprovedNameBean(\"Alice\"): "
+ map.get(new ImprovedNameBean("Alice")));
}
}