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

java 中hashcode和equals 总结

2016-05-07 17:24 495 查看

一、概述

          在Java中hashCode的实现总是伴随着equals,他们是紧密配合的,你要是自己设计了其中一个,就要设计另外一个。当然在多数情况下,这两个方法是不用我们考虑的,直接使用默认方法就可以帮助我们解决很多问题。但是在有些情况,我们必须要自己动手来实现它,才能确保程序更好的运作。

1.1 规则

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

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

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

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

Unequal objects must have different hash codes – WRONG!
Objects with the same hash code must be equal – WRONG!
关于hashcode,你必须知道的三件事

Whenever you implement equals, you MUST also implement hashCode
Never misuse hashCode as a key
Do not use hashCode in distributed applications
详情见:The 3 things you should know about hashCode()

 

对于hashCode,我们应该遵循如下规则

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

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

     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:

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

     2、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()必须相等。”

 

二、equals详解

2.1 equals的设计指导

[code]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)


    {


        // 1、 自反性


        if (other == this)


        {


            return true;


        }


        // 2、判断空值


        if (other == null)


        {


            return false;


        }


        // 3、对象所属类的类型判断


        if (!getClass().equals(other.getClass()))


        {


            return false;


        }


        // 4、对象的类型转换


        Person person = (Person) other;


        // 5、针对所需比较的域进行比较


        if ((name.equals(person.name))&&(age==person.age))


        {


            return true;


        }


   


        return false;


    }


}

[/code]
[/code]

[code]public boolean equals(Object anObject)


{


   // 1、自反性判断


   if (this == anObject)


   {


       return true;


   }


   // 3、类型判断


   if (anObject instanceof String)


   {


       ///4、类型转换


       String anotherString = (String) anObject;


       ///5、针对所需比较的域进行比较


       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;


}

[/code]
[/code]

虽然,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.

 

 

 

三、hashCode详解

3.1 hashCode设计指导

Josh Bloch在他的书籍《Effective Java》告诉我们重写hashcode方法的最佳实践方式。

一个好的hashcode方法通常最好是不相等的对象产生不相等的hash值,理想情况下,hashcode方法应该把集合中不相等的实例均匀分布到所有可能的hash值上面。

下面就详细介绍一下如何设计这个算法。这个算法是有现成的参考的,算法的具体步骤就是:


1、把某个非零常数值(一般取素数),例如17,保存在int变量result中;

2、对于对象中每一个关键域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方法。

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

4、最后返回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代码设计如下:


[code]/** 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;
}


[/code]

 

参考:

1、浅谈Java中的hashcode方法

2、Java中hashCode的作用

3、Java提高篇(二六)——hashCode

4、hashCode与equals的区别与联系

5、Java核心技术卷1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: