java 中hashcode和equals 总结

一、概述

           在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

@Overridepublic 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

时间: 2024-10-23 19:12:30

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

面试点:Java 中 hashCode() 和 equals() 的关系

Java 中 hashCode() 和 equals() 的关系是面试中的常考点,如果没有深入思考过两者设计的初衷,这个问题将很难回答.除了应付面试,理解二者的关系更有助于我们写出高质量且准确的代码. 一.基础:hashCode() 和 equals() 简介 在学习 hashCode() 和 equals() 之间的关系之前, 我们有必要先单独地了解他俩的特点. equals() equals() 方法用于比较两个对象是否相等,它与 == 相等比较符有着本质的不同. 在万物皆对象的 Java

Java 中 hashCode 和 equals 方法 小结

转载:http://www.oschina.net/question/82993_75533 hashCode()和equals()定义在Object类中,这个类是所有java类的基类,所以所有的java类都继承这两个方法. hashcode主要是set集合使用,是用于判断对象是否”可能“相等的快捷办法,以解决大集合的问题.举例来说,如果一个一万个元素的集合加入一个元素,如果是一个新元素,那么必须要equal一万次才能加入.所以采用hashcode,hashcode的思路是如果equal,则ha

Java中hashcode,equals和==

hashcode方法返回该对象的哈希码值. hashCode()方法可以用来来提高Map里面的搜索效率的,Map会根据不同的hashCode()来放在不同的位置,Map在搜索一个对象的时候先通过hashCode()找到相应的位置,然后再根据equals()方法判断这个位置上的对象与当前要插入的对象是不是同一个.若两个对象equals相等,但不在一个区间,根本没有机会进行比较,会被认为是不同的对象. 所以,Java对于eqauls方法和hashCode方法是这样规定的: 1.如果两个对象相同,那么

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

今天下午研究了半天hashcode()和equals()方法,终于有了一点点的明白,写下来与大家分享(zhaoxudong 2008.10.23晚21.36). 1. 首先equals()和hashcode()这两个方法都是从object类中继承过来的. equals()方法在object类中定义如下: public boolean equals(Object obj) { return (this == obj); } 很明显是对两个对象的地址值进行的比较(即比较引用是否相同).但是我们必需清

java中hashcode和equals的区别和联系

HashSet和HashMap一直都是JDK中最常用的两个类,HashSet要求不能存储相同的对象,HashMap要求不能存储相同的键. 那么Java运行时环境是如何判断HashSet中相同对象.HashMap中相同键的呢?当存储了“相同的东西”之后Java运行时环境又将如何来维护呢? 在研究这个问题之前,首先说明一下JDK对equals(Object obj)和hashcode()这两个方法的定义和规范: 在Java中任何一个对象都具备equals(Object obj)和hashcode()

java中hashcode()和equals()的详解 (转)

(zhaoxudong 2008.10.23晚21.36). 1. 首先equals()和hashcode()这两个方法都是从object类中继承过来的. equals()方法在object类中定义如下:   public boolean equals(Object obj) { return (this == obj); } 很明显是对两个对象的地址值进行的比较(即比较引用是否相同).但是我们必需清楚,当String .Math.还有Integer.Double....等这些封装类在使用equ

从语言设计的角度探究Java中hashCode()和equals()的关系

目录 一. 基础: hashCode()和equals()简介 二. 漫谈: 引入hashCode()与equals()之间的关系 三. 解密: 深入理解hashCode()和equals()之间的关系. 四. 验证: 结合HashMap的源码和官方文档, 验证两者的关系. 一. 基础: hashCode()和equals()简介 在学习hashCode()和equals()之间的关系之前, 我们有必要先单独了解他俩自身的特点. equals()方法用于比较两个对象是否相等, 它与"==&quo

java中的==、equals()、hashCode()源码分析(转载)

在java编程或者面试中经常会遇到 == .equals()的比较.自己看了看源码,结合实际的编程总结一下. 1. ==  java中的==是比较两个对象在JVM中的地址.比较好理解.看下面的代码: 1 public class ComAddr{ 2 public static void main(String[] args) throws Exception { 3 String s1 = "nihao"; 4 String s2 = "nihao"; 5 Str

跟王老师学集合(九)Java中hashCode方法与equals方法的用法

Java中对象比较 主讲人:王少华  QQ群号:483773664 学习目标: 掌握java中的==.equals().hashCode()的异同 一.问题 通过前面的学习,我们知道,Java集合有三个大的接口,List接口.Map接口.Set接口,这三个接口的特点是List接口中的元素能重复.Map接口中的key对象不能重复,Set接口中的元素是不可以重复的.那么问题来了,两个元素是否重复是根据什么来判断的. 二.Java中两个对象比较 (一).== 1.Java中,比较简单类型变量用"==&