java 中hashcode和equals 总结

1、概述

           在Java中hashCode的实现老是伴随着equals,他们是紧密配合的,你要是本身设计了其中一个,就要设计另一个。固然在多数状况下,这两个方法是不用咱们考虑的,直接使用默认方法就能够帮助咱们解决不少问题。可是在有些状况,咱们必需要本身动手来实现它,才能确保程序更好的运做。html

1.1 规则

粗略总结一下在JavaDoc中所规定hashcode方法的合约:算法

     Objects that are equal must have the same hash code within a running process数组

   (在程序执行期间,若是两个对象相等,那么它们的哈希值必须相等)app

  注意,下面两条常见错误想法:eclipse

  • Unequal objects must have different hash codes – WRONG!
  • Objects with the same hash code must be equal – WRONG!

关于hashcode,你必须知道的三件事ide

    1. Whenever you implement equals, you MUST also implement hashCode
    2. Never misuse hashCode as a key
    3. Do not use hashCode in distributed applications

详情见:The 3 things you should know about hashCode()性能

 

对于hashCode,咱们应该遵循以下规则this

      1. 在一个应用程序执行期间,若是一个对象的equals方法作比较所用到的信息没有被修改的话,则对该对象调用hashCode方法屡次,它必须始终如一地返回同一个整数。spa

      2. 若是两个对象根据equals(Object o)方法是相等的,则调用这两个对象中任一对象的hashCode方法必须产生相同的整数结果。.net

      3. 若是两个对象根据equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的hashCode方法,不要求产生不一样的整数结果。但若是能不一样,则可能提升散列表的性能。

 

对于equals,咱们必须遵循以下规则

      对称性:若是x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。

      自反性:x.equals(x)必须返回是“true”。

      传递性:若是x.equals(y)返回是“true”,并且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。

      一致性:若是x.equals(y)返回是“true”,只要x和y内容一直不变,无论你重复x.equals(y)多少次,返回都是“true”。

任何状况下,x.equals(null),永远返回是“false”;x.equals(和x不一样类型的对象)永远返回是“false”。

1.2 做用

hashcode:

      常被系统用来快速检索对象。

equals:

      一、若是没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;

      二、String、Date等类对equals方法进行了重写,它们比较的是所指向的对象的内容。

固然在重写equals方法中,能够指定比较对象的地址,若是这样的话,就失去了重写的意义,因此重写,通常是比较对象的内容。

注意:equals方法不能做用于基本数据类型的变量。

1.3 关联

至于hashcode与equals之间的关联关系,咱们只须要记住以下便可:

  •       若是x.equals(y)返回“true”,那么x和y的hashCode()必须相等。
  •       若是x.equals(y)返回“false”,那么x和y的hashCode()有可能相等,也有可能不等。

 

所以,在重写equals方法时,老是重写hashCode方法。改写后equals方法,使得两个不一样的实例在逻辑上是相等的;若是不重写hashCode方法,则hashCode判断两个不一样实例是不一样的;致使违反“若是x.equals(y)返回“true”,那么x和y的hashCode()必须相等。”

 

2、equals详解

2.1 equals的设计指导

public class Person
{
    private String    name;
    private int age;
 
    public String getName()
    {
        return name;
    }
 
    public void setName(String name)
    {
        this.name = name;
    }
 
    public int getAge()
    {
        return age;
    }
 
    public void setAge(int age)
    {
        this.age = age;
    }
    
     @Override
     public boolean equals(Object other)
     {
         // 一、 自反性
         if (other == this)
         {
             return true;
         }
         // 二、判断空值
         if (other == null)
         {
             return false;
         }
         // 三、对象所属类的类型判断
         if (!getClass().equals(other.getClass()))
         {
             return false;
         }
         // 四、对象的类型转换
         Person person = (Person) other;
         // 五、针对所需比较的域进行比较
         if ((name.equals(person.name))&&(age==person.age))
         {
             return true;
         }
    
         return false;
     }
}
在第3点,对象所属类的类型判断,常常有两种方式:
  1. getClass
  2. instanceof

如何选择这两种方式呢?

若是子类可以拥有本身的相等概念,则对称性需求将强制采用getClass进行检测。

若是由超类决定相等的概念,那么就可使用instanceof进行检测,这样能够在不一样子类的对象之间进行相等的比较。

查看经典String中equals的方法,以下:
public boolean equals(Object anObject)
{
    // 一、自反性判断
    if (this == anObject)
    {
        return true;
    }
    // 三、类型判断
    if (anObject instanceof String)
    {
        ///四、类型转换
        String anotherString = (String) anObject;
        ///五、针对所需比较的域进行比较
        int n = value.length;
        if (n == anotherString.value.length)
        {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0)
            {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

虽然,String没有对null值判断,但在其注释有解释:

* Compares this string to the specified object.  The result is {@code
* true} if and only if the argument is not {@code null} and is a {@code
* String} object
that represents the same sequence of characters as this
* object.

 

 

 

3、hashCode详解

3.1 hashCode设计指导

Josh Bloch在他的书籍《Effective Java》告诉咱们重写hashcode方法的最佳实践方式。
一个好的hashcode方法一般最好是不相等的对象产生不相等的hash值,理想状况下,hashcode方法应该把集合中不相等的实例均匀分布到全部可能的hash值上面。
下面就详细介绍一下如何设计这个算法。这个算法是有现成的参考的,算法的具体步骤就是:
一、把某个非零常数值(通常取素数),例如17,保存在int变量result中;
二、对于对象中每个关键域f(指equals方法中考虑的每个域),计算int类型的哈希值c:
  • boolean型,计算(f ? 0 : 1);
  • byte,char,short型,计算(int)f;
  • long型,计算(int) (f ^ (f>>>32));
  • float型,计算Float.floatToIntBits(afloat);
  • double型,计算Double.doubleToLongBits(adouble)获得一个long,而后再执行long型的计算方式;
  • 对象引用,递归调用它的hashCode方法;
  • 数组域,对其中每一个元素调用它的hashCode方法。

三、步骤2中,计算获得的散列码保存到int类型的变量c中,而后再执行result=31*result+c;(其中31能够自选,最好是素数)

四、最后返回result。

核心公式:
result = 基数(31) * result + 哈希值(c:算法步骤2得到)
例如,Person类的hashCode
@Override
public int hashCode()
{
    int result = 17;
    result = 31 * result + age;
    result = 31 * result + stringToHashCode(name);
    return result;
}

private int stringToHashCode(String str)
{
    int result = 17;
    if (str.length()>0)
    {
        char[] value = str.toCharArray();
        for (int i = 0; i < value.length; i++)
        {
            char c = value[i];
            result = 31 * result + c;   
        }
    }
    return result;
}
查看String中hashCode代码设计以下:
/** The value is used for character storage. */
private final char    value[];

/** Cache the hash code for the string */
private int            hash;        // Default to 0

public int hashCode()
{
    int h = hash;
    if (h == 0 && value.length > 0)
    {
        char val[] = value;

        for (int i = 0; i < value.length; i++)
        {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}


 

参考:

一、浅谈Java中的hashcode方法

二、Java中hashCode的做用

三、Java提升篇(二六)——hashCode

四、hashCode与equals的区别与联系

五、Java核心技术卷1

相关文章
相关标签/搜索