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

来源:互联网 发布:极客帮是什么软件 编辑:程序博客网 时间:2024/06/05 02:18

【翻译】玩转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 0
原创粉丝点击