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比较的变量都与某个素数进行计算,使得不同对象计算出来尽量不同而且分散,这样可以减少冲突。关于此的博客网上很多,暂不作复述,待理解更深刻时整理。

      

  • 相关阅读:
    Spring MVC Ajax 嵌套表单数据的提交
    Spring MVC 过滤静态资源访问
    Spring MVC 页面跳转时传递参数
    IDEA Maven 三层架构 2、运行 springMVC
    IDEA Maven 三层架构 1、基本的Archetype 搭建
    EasyUI DataGrid 基于 Ajax 自定义取值(loadData)
    Spring MVC Ajax 复杂参数的批量传递
    Mybatis Sql片段的应用
    在 Tomcat 8 部署多端口项目
    自动升级的设计思路与实现
  • 原文地址:https://www.cnblogs.com/zhizhizhiyuan/p/3602770.html
Copyright © 2011-2022 走看看