您的位置:首页 > 编程语言 > Java开发

JAVA中HashCode和equals方法的使用

2016-10-11 16:28 239 查看
在这篇文章中,我将告诉大家我对hashCode和equals方法的理解。我将讨论他们的默认实现,以及如何正确的重写他们。我也将使用Apache 
Commons提供的工具包做一个实现。 

使用hashCode()和equals()

hashCode()方法被用来获取给定对象的唯一整数。这个整数被用来确定对象被存储在HashTable类似的结构中的位置。默认的,Object类的
hashCode()方法返回这个对象存储的内存地址的编号。

重写默认的实现

如果你不重写这两个方法,将几乎不遇到任何问题,但是有的时候程序要求我们必须改变一些对象的默认实现。

来看看这个例子,让我们创建一个简单的类Employee

public class Employee

{

    private Integer id;

    private String firstname;

    private String lastName;

    private String d
4000
epartment;

 

    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类只是有一些非常基础的属性和getter、setter.现在来考虑一个你需要比较两个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 false in console

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

    }

}

毫无疑问,上面的程序将输出false,但是,事实上上面两个对象代表的是通过一个employee。真正的商业逻辑希望我们返回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());

}

在上面的类中添加这个方法,EauqlsTest将会输出true。 

So are we done?没有,让我们换一种测试方法来看看。 

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

    }

上面的程序输出的结果是两个。如果两个employee对象equals返回true,Set中应该只存储一个对象才对,问题在哪里呢? 

我们忘掉了第二个重要的方法hashCode()。就像JDK的Javadoc中所说的一样,如果重写equals()方法必须要重写hashCode()方法。我们加上下面这
个方法,程序将执行正确。

@Override

 public int hashCode()

 {

    final int PRIME = 31;

    int result = 1;

    result = PRIME * result + getId();

    return result;

 }

使用Apache Commons Lang包重写hashCode() 和equals()方法 

Apache Commons 包提供了两个非常优秀的类来生成hashCode()和equals()方法。看下面的程序。 

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();

    }

 }

如果你使用Eclipse或者其他的IDE,IDE也可能会提供生成良好的hashCode()方法和equals()方法。 

HashCode的作用:

在 Java 集合中有两类,一类是 List,一类是 Set 他们之间的区别就在于 List 集合中的元素师有序的,且可以重复,而 Set 集合中元素是无序不可重复的。对于 List 好处理,但是对于 Set 而言我们要如何来保证元素不重复呢?通过迭代来 equals() 是否相等。数据量小还可以接受,当我们的数据量大的时候效率可想而知(当然我们可以利用算法进行优化)。比如我们向 HashSet 插入 1000 数据,难道我们真的要迭代
1000 次,调用 1000 次 equals() 方法吗?hashCode 提供了解决方案。怎么实现?我们先看 hashCode 的源码(Object)。

     -->
public native int hashCode();
它是一个本地方法,它的实现与本地机器有关,这里我们暂且认为他返回的是对象存储的物理位置(实际上不是,这里写是便于理解)。当我们向一个集合中添加某个元素,集合会首先调用 hashCode 方法,这样就可以直接定位它所存储的位置,若该处没有其他元素,则直接保存。若该处已经有元素存在,就调用 equals 方法来匹配这两个元素是否相同,相同则不存,不同则散列到其他位置(具体情况请参考(Java 提高篇()—–HashMap))。这样处理,当我们存入大量元素时就可以大大减少调用
equals() 方法的次数,极大地提高了效率。

所以 hashCode 在上面扮演的角色为寻域(寻找某个对象在集合中区域位置)。hashCode 可以将集合分成若干个区域,每个对象都可以计算出他们的 hash 码,可以将 hash 码分组,每个分组对应着某个存储区域,根据一个对象的 hash 码就可以确定该对象所存储区域,这样就大大减少查询匹配元素的数量,提高了查询效率。


尽量保证使用对象的同一个属性来生成hashCode()和equals()两个方法。在我们的案例中,我们使用员工id。

eqauls方法必须保证一致(如果对象没有被修改,equals应该返回相同的值)

任何时候只要a.equals(b),那么a.hashCode()必须和b.hashCode()相等。

两者必须同时重写。
当使用ORM的时候特别要注意的:
如果你使用ORM处理一些对象的话,你要确保在hashCode()和equals()对象中使用getter和setter而不是直接引用成员变量。因为在ORM中有的时候
成员变量会被延时加载,这些变量只有当getter方法被调用的时候才真正可用。

例如在我们的例子中,如果我们使用e1.id == e2.id则可能会出现这个问题,但是我们使用e1.getId() == e2.getId()就不会出现这个问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: