java中使用hashCode和equals方法

时间:2023-01-16 16:08:40

在这篇文章中,我将指出我对hashCode()和equals()方法的理解我将讨论它们的默认实现以及如何正确覆盖它们我还将使用Apache Commons包的实用程序类来编写这些方法。

hashCode()并且equals()Object类中定义了方法,它是java对象的父类。为此,所有java对象都继承了这些方法的默认实现。

hashCode()和equals()的使用

hashCode()方法用于为给定对象获取唯一的整数。该整数用于确定桶位置,当此对象需要存储在某些HashTable类数据结构中时。默认情况下,Object的hashCode()方法返回存储对象的内存地址的整数表示形式。

equals()方法,如名称建议,用于简单地验证两个对象的相等性。默认实现只需检查两个对象的对象引用来验证它们的相等性。

覆盖默认行为

一切正常,直到您不要覆盖任何这些方法在您的类中。但是,有时应用程序需要更改某些对象的默认行为。

让我们举个例子,你的应用程序有Emp对象。让我们创建一个最小可能的Emp类结构:

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;
}
}

上面的Emp类有一些非常基本的属性和存在方法。现在考虑一个简单的情况,你需要比较两个员工对象。
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提供了两个优秀的实用类HashCodeBuilderEqualsBuilder来生成哈希码和equals方法。以下是其用途:

maven依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
maven仓库地址:点击打开链接
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.idid字段很可能被懒加载。所以在这种情况下,可能是零或为零,从而导致不正确的行为。

但是如果使用e1.getId()== e2.getId(),我们可以确定,即使字段是懒加载的; 调用getter将首先填充该字段。

这就是我所知道的hashCode()和equals()方法。

注意:为什么重写hashcode系数为31,在网上找到了能解疑的原因。
大概意思是说31这个值是一个奇素数,只是一个默认的传统。并不一定要用31。但是这个数可以通过位移的方式来处理乘法,获得一些性能上的优化,虚拟机会自动做这些优化。如果它是偶数,并且乘法溢出,则信息将丢失,因为乘以2等于移位。

为什么Java的hashCode()在String中使用31作为乘数?点击打开链接

转载地址:点击打开链接