zoukankan      html  css  js  c++  java
  • 关于equals和hashCode

      equals()和hashCode()是Object类的两个函数,重要性可见一斑,不过我们平时使用却未必能深入理解他们。本文从java doc触发,讲到它们与哈希表的关系,再到具体的实现,就我目前掌握的关于这两个函数进行一个梳理。

      一、Java Doc

      Java doc其实远不是只有在编程时查阅API才有用,很多时候它体现了Java的一些设计理念,当然这些理念需要好好分析才能理解。两个函数的具体doc文本可查,不予罗列,只说说重点:

      1. equals():

      a)该方法是在非空对象引用上实现相等关系,具有自反性、对称性、传递性和一致性。这里需要注意“非空”这个词,这说明,任何非空对象不可能equals一个空对象(null),在重写equals的时候,这一点很重要,不然极有可能NullPointerException。

      b)当equals函数被重写时通常有必要重写hashCode()函数,以维护hashCode() 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。这句话很抽象,后面解释。

      2. hashCode():

      a)支持此方法是为了提高哈希表的性能。这说明,该函数从设计上来说就是为了给哈希表使用的!

      b)hashCode协定,简而言之:如果用于equals比较的信息没有更改,那么hashCode必须返回相同的值;如果两个对象是equals的,那么hashCode必须相同;如果两个对象根据equals不等,那么它们hashCode也可以相等,只是不等的hashCode可以提高哈希表性能。

      二、Map、HashMap、HashSet等

      这几个数据结构与上述两个函数之间有非常重要的关系。以Set为例,Set中不能由相同的元素,因此比较就是用equals函数。也就是说,从通常的业务逻辑上来讲,要使用Set,就要重写equals函数,否则会出问题。Map类似。以如下类为例:

      

    public class Student{
        public int stuId;
        public String stuName;
        public boolean gender;
        
        public Student(int id,String name,boolean g)
        {
            stuId=id;
            stuName=name;
            gender=g;
        }
        
        public static void main(String[] args)
        {
            Student stu1=new Student(123,"Tom",true);
            Student stu2=new Student(123,"Tom",true);
            System.out.println(stu1.equals(stu2));
        }
    }

      从业务逻辑上来讲,我们希望上述输出true,但事实上输出false。因此必须重写equals函数,如下:

        @Override
        public boolean equals(Object o){
            if(o==null)
                return false;
            if(o==this)
                return true;
            if(o.getClass!=getClass())
                return false;
            Student s=(Student) o;
            return (stuId==e.stuId);
        }  

      但是加入上述代码仍然不够,假如main函数中有如下代码:

        public static void main(String[] args)
        {
            Student stu1=new Student(123,"Tom",true);
            Student stu2=new Student(123,"Tom",true);
            Set<Student> students=new HashSet<Student>();
            students.add(stu1);
            students.add(stu2);
            System.out.println(students);
        }

      我们是希望students中只有一个对象(Set的定义),然而输出了两个对象。问题何在?问题就在于HashSet的工作流程:为了提高效率,HashSet先通过计算hashCode得到对象应该插入的位置,如果该位置为空,就插入;如果该位置不为空,则比较新插入对象和已有对象是否equals,如果equals返回true,就不插入,如果返回false,则说明发生了冲突,使用解决冲突策略并把新对象插入。上述代码没有重写hashCode函数,两个对象返回不同的hashCode,插入时对应位置都是空的,直接插入了对象,换言之,Set中包含了两个equals返回true的对象,这违反了Set的定义,将给函数带来不可想象的后果。因此必须重写hashCode函数:

      

        @Override
        public int hashCode()
        {
            final int PRIME=31;
            int result=1;
            result=PRIME*result+stuId;
            return result;
        }

      回过头看doc的说明:根据equals不相等的对象,其hashcode不一定非要不同(也即可以相同)。结合HashSet的工作流程,如果他们的hashcode相同,此时必然冲突,但是可以解决冲突后插入,所以不违反Set的定义但是却会影响性能。

      三、如何重写HashCode

      《Effective Java》中有介绍一种简单方法,具体不列,主要思想是使用所有参与equals比较的变量都与某个素数进行计算,使得不同对象计算出来尽量不同而且分散,这样可以减少冲突。关于此的博客网上很多,暂不作复述,待理解更深刻时整理。

      

  • 相关阅读:
    Python KNN算法
    Python TF-IDF计算100份文档关键词权重
    Python 结巴分词
    Python 将pdf转换成txt(不处理图片)
    Python小爬虫-自动下载三亿文库文档
    Kubuntu麦克风音频无声音
    adb常用命令
    Ubuntu下adb的安装
    Wamp安装使用+Git for Windows
    TensorFlow使用记录 (九): 模型保存与恢复
  • 原文地址:https://www.cnblogs.com/zhizhizhiyuan/p/3602770.html
Copyright © 2011-2022 走看看