zoukankan      html  css  js  c++  java
  • 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
    @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

  • 相关阅读:
    ....
    CodeForces 375A(同余)
    POJ 2377 Bad Cowtractors (最小生成树)
    POJ 1258 AgriNet (最小生成树)
    HDU 1016 Prime Ring Problem(全排列)
    HDU 4460 Friend Chains(bfs)
    POJ 2236 Wireless Network(并查集)
    POJ 2100 Graveyard Design(尺取)
    POJ 2110 Mountain Walking(二分/bfs)
    CodeForces 1059B Forgery(模拟)
  • 原文地址:https://www.cnblogs.com/aoguren/p/4823939.html
Copyright © 2011-2022 走看看