• 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

  • 相关阅读:
    [leetcode-551-Student Attendance Record I]
    [leetcode-543-Diameter of Binary Tree]
    [leetcode-541-Reverse String II]
    [leetcode-530-Minimum Absolute Difference in BST]
    [leetcode-521-Longest Uncommon Subsequence I]
    [leetcode-504-Base 7]
    [leetcode-116-Populating Next Right Pointers in Each Node]
    [leetcode-573-Squirrel Simulation]
    [leetcode-572-Subtree of Another Tree]
    [leetcode-575-Distribute Candies]
  • 原文地址:https://www.cnblogs.com/aoguren/p/4823939.html
Copyright © 2020-2023  润新知