一、概述

           在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,你必须知道的三件事

    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,我们应该遵循如下规则

      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的设计指导

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;

     }

}

在第3点,对象所属类的类型判断,经常有两种方式:
  1. getClass
  2. instanceof

如何选择这两种方式呢?

如果子类能够拥有自己的相等概念,则对称性需求将强制采用getClass进行检测。

如果由超类决定相等的概念,那么就可以使用instanceof进行检测,这样可以在不同子类的对象之间进行相等的比较。

查看经典String中equals的方法,如下:
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;

}

虽然,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代码设计如下:
/** 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;
}

 

参考:

1、浅谈Java中的hashcode方法

2、Java中hashCode的作用

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

4、hashCode与equals的区别与联系

5、Java核心技术卷1

java 中hashcode和equals 总结的更多相关文章

  1. java中hashcode()和equals()的详解

    今天下午研究了半天hashcode()和equals()方法,终于有了一点点的明白,写下来与大家分享(zhaoxudong 2008.10.23晚21.36). 1. 首先equals()和hashc ...

  2. Java中hashcode,equals和==

    hashcode方法返回该对象的哈希码值. hashCode()方法可以用来来提高Map里面的搜索效率的,Map会根据不同的hashCode()来放在不同的位置,Map在搜索一个对象的时候先通过has ...

  3. java中hashcode和equals的区别和联系

    HashSet和HashMap一直都是JDK中最常用的两个类,HashSet要求不能存储相同的对象,HashMap要求不能存储相同的键. 那么Java运行时环境是如何判断HashSet中相同对象.Ha ...

  4. java中hashCode()与equals()详解

    首先之所以会将hashCode()与equals()放到一起是因为它们具备一个相同的作用:用来比较某个东西.其中hashCode()主要是用在hash表中提高 查找效率,而equals()则相对而言使 ...

  5. java中 hashCode() 和 equals()

    1. 值类型是存储在内存中的栈,而引用类型的变量在栈中仅仅是存储引用类型变量的地址来自堆,而其本身则存储在栈中. 2. ==操作比较的是两个变量的值是否相等, 3. 对于引用型变量表示的是两个变量在堆 ...

  6. 深入探究Java中hashCode()和equals()的关系

    目录 一.基础:hashCode() 和 equals() 简介 equals() hashCode() 二. 漫谈:初识 hashCode() 与 equals() 之间的关系 三. 解密:深入理解 ...

  7. Java中HashCode()和equals()的作用

    引言 我们知道Java中的集合(Collection)大致可以分为两类,一类是List,再有一类是Set. 前者集合内的元素是有序的,元素可以重复:后者元素无序,但元素不可重复. 这里就引出一个问题: ...

  8. Java中hashCode、equals、==的区别

    ref:http://www.cnblogs.com/skywang12345/p/3324958.html 1.==作用: java中的==用来判断两个对象的地址是否相等:当对象是基本数据类型时,可 ...

  9. java中hashCode和equals什么关系,hashCode到底怎么用的

    Object类的hashCode的用法:(新手一定要忽略本节,否则会很惨) 马 克-to-win:hashCode方法主要是Sun编写的一些数据结构比如Hashtable的hash算法中用到.因为ha ...

随机推荐

  1. 解决MySQL连接超时Communications link failure due to underlying exception

    最近在用一个MySQL的Java连接池的过程中,连接一晚上不释放,第二天就会造成超时的错误,查了一下原因,原来是因为MySQL默认的空闲等待时间是8个小时,一旦空闲超过8个小时,就会抛出异常.异常文本 ...

  2. ASP.net中的Cache使用介绍

    1.1.1 摘要(http://www.cnblogs.com/rush/archive/2012/06/30/2571438.html) 最近我们的系统面临着严峻性能瓶颈问题,这是由于访问量增加,客 ...

  3. Android下得到APK包含信息

    很多情况下,我们需要通过APK文件来得到它的一些信息. (此时此APK不一定被安装了) 0. 基础知识:可以通过android.content.Context的方法 getPackageManager ...

  4. [转]Log4Net中配置文件的解释

    FROM:http://www.cnblogs.com/kissazi2/p/3392605.html 一个完整的配置文件的例子如下所示 <log4net> <!-- 错误日志类-- ...

  5. pip和easy_install使用方式

    easy_install 跟 pip 都是 Python 的套件管理程式,有了它們,在使用 Python 開發程式的時候會帶來不少方便. easy_install 和 pip 有什麼不一樣?據 pip ...

  6. 乐视mysql面试题

      http://blog.itpub.net/28916011/viewspace-2093197/ 最近,朋友去乐视面试了mysql DBA,以下是我据整理的乐视mysql面试题答案,供大家参考 ...

  7. javascript笔记04:let语句 和 yield语句 和 with语句

    1.yield语句: <script type="application/javascript; version=1.7"> function generator() ...

  8. jquery循环遍历radio单选按钮,并设置选中状态

    背景:自己在做项目过程中遇到的问题,现在记录一下. 需求:在ajax获取后台数据的之后,需要根据获取的数据对页面中的radio单选按钮进行选中状态设置 因为自身js功底欠佳,所以耽误了点时间,现在把方 ...

  9. posix thread内存可视性

    线程间的内存可视性 当线程调用怕thread——create时, 它所能看到的内存值也是它建立的线程能够看到的.任何在调用怕thread_create之后写入的数据,可能不会被建立的线程看到,即使写操 ...

  10. APUE(3)——文件I/O

    大多数情况下,我们都会利用Standard I/O Library来进行I/O操作,而这一章所讲的I/O是UNIX系统直接提供的I/O操作,且大多是Unbuffered I/O,即每一次读或写都会出现 ...