最近我读了这篇文章 开发人员工作文件。
该文档是关于有效和正确地定义hashCode()和equals(),但我无法弄清楚为什么我们需要覆盖这两个方法。
我如何决定有效地实现这些方法?
最近我读了这篇文章 开发人员工作文件。
该文档是关于有效和正确地定义hashCode()和equals(),但我无法弄清楚为什么我们需要覆盖这两个方法。
我如何决定有效地实现这些方法?
当前回答
String类和包装器类的equals()和hashCode()方法实现与Object类不同。Object类的equals()方法比较对象的引用,而不是内容。Object类的hashCode()方法为每个对象返回不同的hashCode,无论内容是否相同。
It leads problem when you use Map collection and the key is of Persistent type, StringBuffer/builder type. Since they don't override equals() and hashCode() unlike String class, equals() will return false when you compare two different objects even though both have same contents. It will make the hashMap storing same content keys. Storing same content keys means it is violating the rule of Map because Map doesnt allow duplicate keys at all. Therefore you override equals() as well as hashCode() methods in your class and provide the implementation(IDE can generate these methods) so that they work same as String's equals() and hashCode() and prevent same content keys.
你必须重写hashCode()方法和equals(),因为equals()根据hashCode工作。
此外,与equals()一起重写hashCode()方法有助于完好无损equals()-hashCode()契约:“如果两个对象相等,那么它们必须具有相同的哈希码。”
什么时候需要为hashCode()编写自定义实现?
正如我们所知,HashMap的内部工作是基于哈希原理的。条目集存储在特定的存储桶中。您可以根据自己的需求定制hashCode()实现,以便相同的类别对象可以存储在相同的索引中。 当你使用put(k,v)方法将值存储到Map集合中时,put()的内部实现是:
put(k, v){
hash(k);
index=hash & (n-1);
}
意思是,它生成索引,索引是基于特定键对象的hashcode生成的。所以让这个方法根据你的需求生成哈希码,因为相同的哈希码条目集将存储在相同的桶或索引中。
就是这样!
其他回答
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()时,它可以比较内容并给出正确的结果。
String类和包装器类的equals()和hashCode()方法实现与Object类不同。Object类的equals()方法比较对象的引用,而不是内容。Object类的hashCode()方法为每个对象返回不同的hashCode,无论内容是否相同。
It leads problem when you use Map collection and the key is of Persistent type, StringBuffer/builder type. Since they don't override equals() and hashCode() unlike String class, equals() will return false when you compare two different objects even though both have same contents. It will make the hashMap storing same content keys. Storing same content keys means it is violating the rule of Map because Map doesnt allow duplicate keys at all. Therefore you override equals() as well as hashCode() methods in your class and provide the implementation(IDE can generate these methods) so that they work same as String's equals() and hashCode() and prevent same content keys.
你必须重写hashCode()方法和equals(),因为equals()根据hashCode工作。
此外,与equals()一起重写hashCode()方法有助于完好无损equals()-hashCode()契约:“如果两个对象相等,那么它们必须具有相同的哈希码。”
什么时候需要为hashCode()编写自定义实现?
正如我们所知,HashMap的内部工作是基于哈希原理的。条目集存储在特定的存储桶中。您可以根据自己的需求定制hashCode()实现,以便相同的类别对象可以存储在相同的索引中。 当你使用put(k,v)方法将值存储到Map集合中时,put()的内部实现是:
put(k, v){
hash(k);
index=hash & (n-1);
}
意思是,它生成索引,索引是基于特定键对象的hashcode生成的。所以让这个方法根据你的需求生成哈希码,因为相同的哈希码条目集将存储在相同的桶或索引中。
就是这样!
为了在HashMap, Hashtable等集合中使用我们自己的类对象作为键。,我们应该通过了解集合的内部工作来重写这两个方法(hashCode()和equals())。否则,它会导致我们意想不到的错误结果。
在这个回答中没有提到测试equals/hashcode契约。
我发现EqualsVerifier库非常有用和全面。它也很容易使用。
另外,从头构建equals()和hashCode()方法涉及大量样板代码。Apache Commons Lang库提供了EqualsBuilder和HashCodeBuilder类。这些类极大地简化了复杂类的equals()和hashCode()方法的实现。
顺便说一句,值得考虑重写toString()方法以帮助调试。Apache Commons Lang库提供了ToStringBuilder类来帮助实现这一点。
如果重写equals()而不是hashcode(),则不会发现任何问题,除非您或其他人在HashSet等散列集合中使用该类类型。 在我之前的人已经清楚地解释了很多次文献理论,我只是在这里提供一个非常简单的例子。
考虑一个类,它的equals()需要表示自定义的东西:-
public class Rishav {
private String rshv;
public Rishav(String rshv) {
this.rshv = rshv;
}
/**
* @return the rshv
*/
public String getRshv() {
return rshv;
}
/**
* @param rshv the rshv to set
*/
public void setRshv(String rshv) {
this.rshv = rshv;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Rishav) {
obj = (Rishav) obj;
if (this.rshv.equals(((Rishav) obj).getRshv())) {
return true;
} else {
return false;
}
} else {
return false;
}
}
@Override
public int hashCode() {
return rshv.hashCode();
}
}
现在考虑这个主类:-
import java.util.HashSet;
import java.util.Set;
public class TestRishav {
public static void main(String[] args) {
Rishav rA = new Rishav("rishav");
Rishav rB = new Rishav("rishav");
System.out.println(rA.equals(rB));
System.out.println("-----------------------------------");
Set<Rishav> hashed = new HashSet<>();
hashed.add(rA);
System.out.println(hashed.contains(rB));
System.out.println("-----------------------------------");
hashed.add(rB);
System.out.println(hashed.size());
}
}
这将产生以下输出:-
true
-----------------------------------
true
-----------------------------------
1
我对结果很满意。但是如果我没有覆盖hashCode(),它将导致噩梦,因为具有相同成员内容的Rishav对象将不再被视为唯一的hashCode将是不同的,因为由默认行为生成,这里将是输出:-
true
-----------------------------------
false
-----------------------------------
2