<八>hashCode()和equals()方法

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

 

hashCode()和equals()方法已经在Object类中定义,而Object类是所有java对象的父类。因为这个原因,所有的Java对象从Object类继承了这些方法的默认实现。

本文中的章节:

  • hashCode()和equals()的用法
  • 重写默认行为
  • 用Apache Commons Lang重写equals()和hashCode()
  • 需要记住的重要事项
  • 在ORM中使用时需要注意的地方(对象关系映射(Object Relational Mapping,简称ORM))
  • 为什么重写对象equals()方法需要重写hashCode()方法的真正原因

hashCode()和equals()方法的用法

hashCode()方法用于获取给定对象的唯一的整数。当这个对象需要存储在哈希表这样的数据结构时,这个整数用于确定桶的位置。默认情况下,对象的hashCode()方法返回对象所在内存地址的整数表示

equals()方法用来简单验证两个对象的相等性。默认实现只检查两个对象的引用,以验证它们的相等性。

重写默认行为

通常情况下,类一切运行的都很好,根本不需要重写它们,但是有时应用程序需要更改某些对象的默认行为。

举个例子,比如有一个应用程序拥有员工对象。我们创建一个尽可能简单的员工类:

public class Employee
{
    private Integer id;
    private String firstname;
    private String lastName;
    private String department;
 
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getFirstname() {
        return firstname;
    }
    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getDepartment() {
        return department;
    }
    public void setDepartment(String department) {
        this.department = department;
    }
}

上面的员工类有一些非常基本的字段和get/set方法。

现在考虑一个简单的情况,需要比较两个员工对象。

public class EqualsTest {
    public static void main(String[] args) {
        Employee e1 = new Employee();
        Employee e2 = new Employee();
 
        e1.setId(100);
        e2.setId(100);
 
        //Prints false in console
        System.out.println(e1.equals(e2));
    }
}

不用猜,默认情况下,以上方法将输出"false"。但是,这两个对象现实生活中代表同一雇员。在实际应用程序中,必须返回true。

要实现正确的行为,我们需要如下重载equals方法:

public boolean equals(Object o){
    if(o == null){
        return false;
    }
    if(o == this){
        return true;
    }
    if(getClass() != o.getClass()){
        return false;
    }
    Employee e = (Employee)o;
    return (this.getId() == e.getId());//==比较的是地址和内容
}

这个方法添加到你的员工,开始运行EqualTest,将打印true

这就完了吗?还没有。再以不同的方式再次测试上面修改过的员工类。

import Java.util.HashSet;
import Java.util.Set;
 
public class EqualsTest
{
    public static void main(String[] args)
    {
        Employee e1 = new Employee();
        Employee e2 = new Employee();
 
        e1.setId(100);
        e2.setId(100);
 
        //Prints 'true'
        System.out.println(e1.equals(e2));
 
        Set<Employee> employees = new HashSet<Employee>();
        employees.add(e1);
        employees.add(e2);
         
        //Prints two objects
        System.out.println(employees);
    }
}

上面的类在第二个打印语句中打印两个对象。如果员工对象已经相等,在一个set集合中应该只存在一个对象,HashSet中该只有一个实例才对,毕竟对象指的是同一名员工。哪里出现问题了呢

问题出现在我们没有重写第二个重要的方法hashCode()Java文档中说,如果重写equals()方法,然后你必须重写hashCode()方法。因此,让我们在员工类中添加另一个方法。

@Override
public int hashCode(Object o){
    final int PRIME = 31;
    int result = 1;
    Employee e = (Employee) o;
    result = PRIME * result + e.getId();
    return result;
}

一旦在雇员类中添加了上述方法,第二个语句就开始在第二个语句中只打印单个对象,从而验证了e1和e2的真正相等性。

用Apache Commons Lang 重写equals()和hashCode()

Apache Commons提供两个优秀的实用类HashCodeBuilder和EqualsBuilder用于生成hashCode和测试相等性的代码。下面是他们的使用:

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Employee
{
    private Integer id;
    private String firstname;
    private String lastName;
    private String department;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getFirstname() {
        return firstname;
    }
    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getDepartment() {
        return department;
    }
    public void setDepartment(String department) {
        this.department = department;
    }
    @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;
     
    Employee e = (Employee) o;
     
    return new EqualsBuilder().
              append(getId(), e.getId()).
              isEquals();
    }
}

或者,用代码编辑器自动生成,它们必然能够为您生成一些良好的代码。例如。myeclipse中,在类上右键>source>Generate hashCode() and equals(),它会为你生成一个很好的实现。

需要记住的重要事项

  1. 始终使用同一个对象的相同字段来生成hashCode()和equals()。
  2. equals()必须一致(如果对象不被修改,那么它必须返回相同的值)。
  3. 每当a.equals(b),那么a.hashCode()和b.hashCode()必须相等
  4. 如果重写一个,则必须重写另一个。

ORM中使用时需要注意的地方

如果你正在处理一个ORM,确保hashCode()和equals()中始终使用getters,不要直接使用字段。这是因为在ORM中,偶尔字段被延迟加载,直到调用它们的getter方法时才可用。

例如我们的员工类,如果我们使用e1.id == e2.id。id字段非常可能是懒加载。因此,在这种情况下,一个可能是零或null,从而导致不正确的行为。

但是如果采用e1.getId() == e2.getId(),我们可以肯定,即使是懒加载,调用getter将首先让字段加载正确。

为什么重写对象equals()方法需要重写hashCode()方法的真正原因:见下一章节https://blog.csdn.net/fraya1234/article/details/118106484

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值