最近我读了这篇文章 开发人员工作文件。
该文档是关于有效和正确地定义hashCode()和equals(),但我无法弄清楚为什么我们需要覆盖这两个方法。
我如何决定有效地实现这些方法?
最近我读了这篇文章 开发人员工作文件。
该文档是关于有效和正确地定义hashCode()和equals(),但我无法弄清楚为什么我们需要覆盖这两个方法。
我如何决定有效地实现这些方法?
当前回答
public class Employee {
private int empId;
private String empName;
public Employee(int empId, String empName) {
super();
this.empId = empId;
this.empName = empName;
}
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
@Override
public String toString() {
return "Employee [empId=" + empId + ", empName=" + empName + "]";
}
@Override
public int hashCode() {
return empId + empName.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(this instanceof Employee)) {
return false;
}
Employee emp = (Employee) obj;
return this.getEmpId() == emp.getEmpId() && this.getEmpName().equals(emp.getEmpName());
}
}
测试类
public class Test {
public static void main(String[] args) {
Employee emp1 = new Employee(101,"Manash");
Employee emp2 = new Employee(101,"Manash");
Employee emp3 = new Employee(103,"Ranjan");
System.out.println(emp1.hashCode());
System.out.println(emp2.hashCode());
System.out.println(emp1.equals(emp2));
System.out.println(emp1.equals(emp3));
}
}
在对象类中,equals(Object obj)用于比较地址比较,这就是为什么在Test类中,如果你比较两个对象,则equals method给出false,但当我们重写hashcode()时,它可以比较内容并给出正确的结果。
其他回答
Joshua Bloch在Effective Java上说
必须在重写equals()的每个类中重写hashCode()。如果不这样做,将违反Object.hashCode()的一般约定,这将阻止您的类与所有基于哈希的集合(包括HashMap、HashSet和Hashtable)一起正常运行。
让我们通过一个例子来理解它,如果我们重写equals()而不重写hashCode()并尝试使用Map会发生什么。
假设我们有这样一个类,如果MyClass的两个对象的importantField相等(使用eclipse生成的hashCode()和equals()),则MyClass的两个对象相等
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)。所以,尽管它们是相等的,因为它们不散列到同一个桶,映射无法意识到这一点,它们都留在映射中。
虽然如果重写hashCode()就没有必要重写equals(),但让我们看看在这种特殊情况下会发生什么:我们知道MyClass的两个对象是相等的,如果它们的importantField相等,但我们没有重写equals()。
只覆盖hashCode
如果你只覆盖hashCode,那么当你调用myMap.put(first,someValue)时,它首先接受,计算它的hashCode并将其存储在给定的bucket中。然后,当您调用myMap.put(second,someOtherValue)时,它应该根据Map文档将first替换为second,因为它们是相等的(根据业务需求)。
但问题是,equals没有被重新定义,所以当map哈希second并遍历bucket时,寻找是否有一个对象k,使得second.equals(k)为真,它不会找到任何对象,因为second.equals(first)将为假。
希望这是清楚的
在下面的例子中,如果您注释掉Person类中equals或hashcode的覆盖,此代码将无法查找Tom的订单。使用哈希码的默认实现可能会导致哈希表查找失败。
下面是一个简化的代码,它按Person提取人们的订单。Person被用作哈希表中的键。
public class Person {
String name;
int age;
String socialSecurityNumber;
public Person(String name, int age, String socialSecurityNumber) {
this.name = name;
this.age = age;
this.socialSecurityNumber = socialSecurityNumber;
}
@Override
public boolean equals(Object p) {
//Person is same if social security number is same
if ((p instanceof Person) && this.socialSecurityNumber.equals(((Person) p).socialSecurityNumber)) {
return true;
} else {
return false;
}
}
@Override
public int hashCode() { //I am using a hashing function in String.java instead of writing my own.
return socialSecurityNumber.hashCode();
}
}
public class Order {
String[] items;
public void insertOrder(String[] items)
{
this.items=items;
}
}
import java.util.Hashtable;
public class Main {
public static void main(String[] args) {
Person p1=new Person("Tom",32,"548-56-4412");
Person p2=new Person("Jerry",60,"456-74-4125");
Person p3=new Person("Sherry",38,"418-55-1235");
Order order1=new Order();
order1.insertOrder(new String[]{"mouse","car charger"});
Order order2=new Order();
order2.insertOrder(new String[]{"Multi vitamin"});
Order order3=new Order();
order3.insertOrder(new String[]{"handbag", "iPod"});
Hashtable<Person,Order> hashtable=new Hashtable<Person,Order>();
hashtable.put(p1,order1);
hashtable.put(p2,order2);
hashtable.put(p3,order3);
//The line below will fail if Person class does not override hashCode()
Order tomOrder= hashtable.get(new Person("Tom", 32, "548-56-4412"));
for(String item:tomOrder.items)
{
System.out.println(item);
}
}
}
hashCode()方法用于获取给定对象的唯一整数。这个整数用于确定桶的位置,当这个对象需要存储在一些HashTable, HashMap之类的数据结构时。默认情况下,Object的hashCode()方法返回存储Object的内存地址的整数表示形式。
对象的hashCode()方法用于将对象插入到HashTable、HashMap或HashSet中。更多关于哈希表的信息请参见Wikipedia.org。
要在map数据结构中插入任何条目,我们需要键和值。如果键和值都是用户定义的数据类型,则键的hashCode()将确定在内部将对象存储在何处。当还需要从映射中查找对象时,键的哈希码将确定在哪里搜索对象。
哈希码只在内部指向某个“区域”(或列表,桶等)。因为不同的键对象可能具有相同的哈希码,所以哈希码本身并不能保证找到正确的键。然后哈希表迭代该区域(具有相同哈希代码的所有键),并使用键的equals()方法找到正确的键。一旦找到正确的键,就会返回为该键存储的对象。
因此,正如我们所看到的,hashCode()和equals()方法的组合在哈希表中存储和查找对象时使用。
注:
总是使用对象的相同属性来生成hashCode()和equals()。在我们的例子中,我们使用了员工id。 Equals()必须是一致的(如果对象没有被修改,那么它必须一直返回相同的值)。 当a. = (b)时,则a.hashCode()必须与b. hashcode()相同。 如果你覆盖了一个,那么你也应该覆盖另一个。
http://parameshk.blogspot.in/2014/10/examples-of-comparable-comporator.html
恕我冒昧,这是根据规则说的-如果两个对象相等,那么它们应该具有相同的哈希值,即相等的对象应该产生相同的哈希值。
如上所述,Object is ==中的默认equals()对地址进行比较,hashCode()返回整数形式的地址(对实际地址进行哈希),这对于不同的Object来说也是不同的。
如果你需要在基于哈希的集合中使用自定义对象,你需要覆盖equals()和hashCode(),例如如果我想维护员工对象的HashSet,如果我不使用更强的hashCode和equals,我可能最终会覆盖两个不同的员工对象,这发生在我使用年龄作为hashCode()时,但是我应该使用唯一的值,可以是员工ID。
hashCode ():
如果只重写hash-code方法,什么也不会发生,因为它总是为每个对象返回一个新的hashCode作为object类。
equals ():
如果你只覆盖equals方法,如果a.equals(b)为真,这意味着a和b的hashCode必须是相同的,但这不会发生,因为你没有覆盖hashCode方法。
注意:Object类的hashCode()方法总是为每个对象返回一个新的hashCode。
因此,当您需要在基于哈希的集合中使用对象时,必须重写equals()和hashCode()。