在这篇文章中,我将指出我对hashCode()和equals()方法的理解。我将讨论它们的默认实现以及如何正确覆盖它们。我还将使用Apache Commons包的实用程序类来编写这些方法。
hashCode()
并且equals()
在Object
类中定义了方法,它是java对象的父类。为此,所有java对象都继承了这些方法的默认实现。
hashCode()和equals()的使用
hashCode()
方法用于为给定对象获取唯一的整数。该整数用于确定桶位置,当此对象需要存储在某些HashTable类数据结构中时。默认情况下,Object的hashCode()方法返回存储对象的内存地址的整数表示形式。
equals()
方法,如名称建议,用于简单地验证两个对象的相等性。默认实现只需检查两个对象的对象引用来验证它们的相等性。
覆盖默认行为
一切正常,直到您不要覆盖任何这些方法在您的类中。但是,有时应用程序需要更改某些对象的默认行为。
让我们举个例子,你的应用程序有Emp对象。让我们创建一个最小可能的Emp类结构:
public class Emp上面的Emp类有一些非常基本的属性和存在方法。现在考虑一个简单的情况,你需要比较两个员工对象。
{
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class EqualsTest {
public static void main(String[] args) {
Empl e1 = new Empl();
Empl e2 = new Empl();
e1.setId(100);
e2.setId(100);
//控制台打印false
System.out.println(e1.equals(e2));
}
}
以上方法将打印“ false ”。但是,在知道这两个对象代表同一个员工之后真的是正确的。在实时应用程序中,这必须返回true。
为了达到正确的行为,我们需要重写equals方法,如下所示:
@override
public boolean equals(Object o) {
if(o == null)
{
return false;
}
if (o == this)
{
return true;
}
if (getClass() != o.getClass())
{
return false;
}
Emp e = (Emp) o;
return (this.getId() == e.getId());
}
将此方法添加到您的Emp类中,EqualsTest并将开始返回“ true ”。
那么我们做了 还没。让我们Employee
以不同的方式再次测试修改后的类。
import java.util.HashSet;
import java.util.Set;
public class EqualsTest
{
public static void main(String[] args)
{
Emp e1 = new Emp();
Emp e2 = new Emp();
e1.setId(100);
e2.setId(100);
//打印 'true'
System.out.println(e1.equals(e2));
Set<Emp> empSet = new HashSet<Emp>();
empSet.add(e1);
empSet.add(e2);
//打印两个不同的对象
System.out.println(empSet);
}
}
上面的类在第二个print语句中打印两个对象。如果两个员工对象相等,Set
则只存储唯一对象的内部必须只有一个实例HashSet
,所有这两个对象都引用同一个员工。我们失踪了什么?
我们缺少第二个重要的方法hashCode()
。正如java文档所说,如果你重写equals()
方法,那么你必须覆盖hashCode()
方法。所以我们可以在我们的Employee
类中添加另一个方法。
@Override
public int hashCode()
{
final int PRIME = 31;
int result = 1;
result = PRIME * result + getId();
return result;
}
一旦在Emp
类中添加了上述方法,第二个语句开始仅在第二个语句中打印单个对象,从而验证e1和e2的真实相等性。
使用Apache Commons Lang覆盖hashCode()和equals()
Apache commons提供了两个优秀的实用类HashCodeBuilder和EqualsBuilder来生成哈希码和equals方法。以下是其用途:
maven依赖:<dependency>maven仓库地址:点击打开链接
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Emp
{
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode()
{
final int PRIME = 31;
return new HashCodeBuilder(getId()%2==0?getId()+1:getId(), PRIME).toHashCode();
}
@Override
public boolean equals(Object o) {
if (o == null)
return false;
if (o == this)
return true;
if (o.getClass() != getClass())
return false;
Emp e = (Emp) o;
return new EqualsBuilder().
append(getId(), e.getId()).
isEquals();
}
}
要记住的重要事项
1)始终使用对象的相同属性来生成hashCode()
和equals()
两者。在我们的例子中,我们使用了员工ID。
2)equals()
必须一致(如果对象未被修改,那么它必须保持返回相同的值)。
3)每当a.equals(b),那么a.hashCode()必须与b.hashCode()相同。
4)如果你重写一个,那么你应该覆盖另一个。
在ORM中使用时特别注意
如果你正在处理一个ORM,确保始终用干将,并在现场从不引用hashCode()
和equals()
。这是出于原因,在ORM中,有时字段是惰性加载的,直到被称为getter方法才可用。
例如,在我们Employee
班,如果我们使用e1.id == e2.id。id字段很可能被懒加载。所以在这种情况下,可能是零或为零,从而导致不正确的行为。
但是如果使用e1.getId()== e2.getId(),我们可以确定,即使字段是懒加载的; 调用getter将首先填充该字段。
这就是我所知道的hashCode()和equals()方法。