【翻译】玩转Java的hashCode()与equals()方法

【翻译】玩转Java的 hashCode()equals()方法

原文地址: Working with hashCode and equals methods in java

本文将讨论Java中的 hashCode()eqauls()方法的默认实现,如何正确地重写,以及如何使用 Apache Commons包的一些工具类。 hashCode()eqauls()方法在 Object类中定义的,因此,所有的Java对象继承了这两个方法的默认实现。

一、hashCode()eqauls()方法的使用

hashCode()方法用来获取给定对象的特定整型值(哈希码)。在像HashMap类似的数据结果中,这个整数用来确定对象存储在哪个哈希桶(Hash bucket)中。默认情况下,对象的hashCode()方法返回的这个整型值代表了对象存储的内存地址。
equals()方法用来判断两个对象是否相等。默认的实现仅简单地检查两个对象的引用地址是否相等。

二、重写默认的行为

如果我们在自定义的类中不重写任何方法,可以确保所有的方法运行正常。但是,在应用软件中经常需要去改变一些对象的默认行为。让我们来看一个包括Employee类的代码实例。首先,让我们来创建一个Employee类:

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

上面的Employee类封闭了一些基本的属性及其访问方法。现在,我们来考虑一个简单的场景,即,我们需要判断两个Employee对象是否相等。

public class EqualsTest {
    public static void main(String[] args) {
        Employee e1 = new Employee();
        Employee e2 = new Employee();

        e1.setId(100);
        e2.setId(100);

        System.out.println(e1.equals(e2));
    }
}  

上述代码将返回“false”。但是从实际情况来看,这两个对象代表的其实是同一个employee,因此,在我们的应用中应该返回true
为了实现上述的行为,我们需要按如下的方式重写equals()方法:

    @Override
    public boolean equals(Object o) {
        if(o == null){
            return false;
        }

        if(o == this){
            return true;
        }

        if(o instanceof Employee){
            Employee e = (Employee) o;
            return (this.getId() == e.getId());
        }
        return false;
    }  

    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return "Employee:[id: "+this.getId() + "]";
    }  

将上述方法添加到Employee类后,EqualsTest将返回true
到此,我们就完成了所有工作了吗?当然不是。让我们用不同的方法来对测试一下修改后的Employee类。

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<>();
        employees.add(e1);
        employees.add(e2);
        //Prints two objects.
        System.out.println(employees);
    }
}  

输出结果:

true
[Employee:[id: 100], Employee:[id: 100]]

上述代码打印HashSet对象employees时打印了两个。如果两个employees对象相等,那么Set中应该只保存一个对象。什么地方出错了呢?
我们还需要重写另一个重要的方法hashCode()。如Java Docs中所描述的,如果我们重写equals()方法,那么我们必须同时重写hashCode()方法。因此,让我们在Employee类中添加另一个方法。

    @Override
    public int hashCode() {
        // TODO Auto-generated method stub
        final int PRIME = 31;
        int result = 1;
        result = PRIME * result + getId().hashCode();
        return result;
    }  

执行EqualsTest,输出结果:

true
[Employee:[id: 100]]  

当我们在Employee类中添加了上述方法,EqualsTest````HashSet对象employees时仅打印了一个对象,从而证明e1e2相等性。

三、使用Apache Commons Lang重写hashCode()equals()方法

Apache Commons提供了两个非常优化的工具类HashCodeBuilderEqualsBuilder,用来生成hashCode()equals()方法。下面是其使用方法:

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.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 boolean equals(Object o) {
        if(o == null){
            return false;
        }

        if(o == this){
            return true;
        }

        if(o instanceof Employee){
            Employee e = (Employee) o;
            return new EqualsBuilder().append(getId(), e.getId()).isEquals();
        }
        return false;
    }

    @Override
    public int hashCode() {
        final int PRIME = 31;
        return new HashCodeBuilder(getId() % 2 == 0 ? getId() + 1 : getId(), PRIME).toHashCode();
    }
    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return "Employee:[id: "+this.getId() + "]";
    }
}  

此外,如果我们使用集成开发环境(IDE),我们也可以利用IDE来自动生成一个结构良好的代码。例如:在Eclipse IDE中可以右击**class>>source>>Generate hashCode() and equals()…,就可以为我们生成实现良好的代码。

四、需要记住的关键点

  1. 通常需要使用同一个属性来生成hashCode()equals()方法。在上面的示例中,我们使用的是Employee id
  2. 需要保证equals()方法返回值的一致性。即,如果对象没有被修改,那么,多次调用equals()方法比较时返回同样的值。
  3. a.equals(b)返回true时,a.hashCode()b.hashCode()必须相等。
  4. 如果我们重写了equals()hashCode()两个方法中的任意一个,则必须重写另一个。

五、在ORM中使用的特殊点

**Info: **ORM,Object Relation Mapping,对象关系映射,用于实现面向对象编程语言里不同类型系统的数据之间的转换。

我们在处理对象关系映射时,在hashCode()equals()方法中需要确保访问对象属性时使用getters,而不是使用属性引用。因为,在对象关系映射的处理中,有时会使用迟延加载来实例化属性,对象属性直到调用getter方法时才可用。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值