Joshua Bloch谈有效Java
您必须在覆盖 equals() 的每个类中重写 hashCode()。如果不这样做,将导致违反 Object.hashCode() 的一般协定,这将阻止您的类与所有基于哈希的集合(包括 HashMap、HashSet 和 Hashtable)一起正常运行。
让我们尝试通过一个示例来理解它,如果我们在不覆盖的情况下覆盖并尝试使用 .equals()
hashCode()
Map
假设我们有一个这样的类,如果两个对象相等,则它们的相等(与日食一起并由日食生成)MyClass
importantField
hashCode()
equals()
public class MyClass {
private final String importantField;
private final String anotherField;
public MyClass(final String equalField, final String anotherField) {
this.importantField = equalField;
this.anotherField = anotherField;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((importantField == null) ? 0 : importantField.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final MyClass other = (MyClass) obj;
if (importantField == null) {
if (other.importantField != null)
return false;
} else if (!importantField.equals(other.importantField))
return false;
return true;
}
}
想象一下你有这个
MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");
仅覆盖等于
如果仅被覆盖,那么当您首先调用时,将哈希到某个存储桶,当您调用它时,它将哈希到其他某个存储桶(因为它们具有不同的)。因此,尽管它们是相等的,但由于它们不散列到同一个存储桶,因此地图无法实现它,并且它们都保留在地图中。equals
myMap.put(first,someValue)
myMap.put(second,someOtherValue)
hashCode
尽管如果我们覆盖,则不必重写,让我们看看在这种特殊情况下会发生什么,我们知道如果两个对象相等,则它们相等,但我们不覆盖。equals()
hashCode()
MyClass
importantField
equals()
仅覆盖哈希代码
如果您只覆盖,那么当您调用它时,首先需要,计算它并将其存储在给定的存储桶中。然后,当您调用它时,应根据地图文档将first替换为sec,因为它们是相等的(根据业务需求)。hashCode
myMap.put(first,someValue)
hashCode
myMap.put(second,someOtherValue)
但问题是,equals 没有被重新定义,所以当映射散列并循环访问存储桶时,查找是否存在这样的对象,它不会找到任何对象。second
k
second.equals(k)
second.equals(first)
false
希望它很清楚
集合(如 和)使用对象的哈希码值来确定应如何将其存储在集合中,并再次使用哈希代码以在其集合中查找对象。HashMap
HashSet
哈希检索过程分为两个步骤:
- 找到合适的存储桶(使用
hashCode()
) - 在存储桶中搜索正确的元素 (使用
equals()
)
这里有一个小例子,说明为什么我们应该过度和.equals()
hashcode()
考虑一个具有两个字段的类:年龄和名称。Employee
public class Employee {
String name;
int age;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (!(obj instanceof Employee))
return false;
Employee employee = (Employee) obj;
return employee.getAge() == this.getAge()
&& employee.getName() == this.getName();
}
// commented
/* @Override
public int hashCode() {
int result=17;
result=31*result+age;
result=31*result+(name!=null ? name.hashCode():0);
return result;
}
*/
}
现在创建一个类,将对象插入到 中,并测试该对象是否存在。Employee
HashSet
public class ClientTest {
public static void main(String[] args) {
Employee employee = new Employee("rajeev", 24);
Employee employee1 = new Employee("rajeev", 25);
Employee employee2 = new Employee("rajeev", 24);
HashSet<Employee> employees = new HashSet<Employee>();
employees.add(employee);
System.out.println(employees.contains(employee2));
System.out.println("employee.hashCode(): " + employee.hashCode()
+ " employee2.hashCode():" + employee2.hashCode());
}
}
它将打印以下内容:
false
employee.hashCode(): 321755204 employee2.hashCode():375890482
现在取消注释方法,执行相同的操作,输出将是:hashcode()
true
employee.hashCode(): -938387308 employee2.hashCode():-938387308
现在你能明白为什么如果两个对象被认为是相等的,它们的哈希码也必须相等吗?否则,您将永远无法找到该对象,因为类 Object 中的默认哈希码方法几乎总是为每个对象提供一个唯一的数字,即使该方法被覆盖,以至于两个或多个对象被视为相等。如果对象的哈希码没有反映这一点,那么它们的相等程度并不重要。所以再一次:如果两个对象相等,它们的哈希码s也必须相等。equals()
-
为 equals() 实现选择字段的最佳实践 在编写单元测试时,我经常会遇到这样的情况:测试中的某个对象 (in - - 应该与它在实际环境中的工作方式不同。以一些接口为例。它有和其他几个领域。从逻辑上讲,当一个配置匹配时,它
-
-
字符串在Android上的Java中似乎并不相等,即使它们打印相同 我有一个问题,我对此感到困惑。我的Android应用程序中有以下代码行: 当我查看第一个 println 语句的输出时,它在 LogCat 中显示为“start”(显然没有引号)。但是,当 if 语句执行时,它会转
-
匹配和等于之间的差异在字符串类中区分大小写或等于 matchs:将检查输入的完整字符串是否等于字符串对象中存在的值。 equalsIgnoreCase:忽略大小写,它会检查输入的字符串是否等于字符串对象中存在的值。 equals:区分大小写,它会检查输入的字
-
Java Hashset.contains() 产生神秘的结果 我通常不用Java编写代码,但最近我开始别无选择。我可能对如何正确使用HashSet有一些重大误解。因此,我所做的一些事情可能完全是错误的。但是,我很感激您可能提供的任何帮助。所以实际