Java中的hashCode()和equals()

参考文章:

从一道面试题彻底搞懂hashCode与equals的作用与区别及应当注意的细节

浅谈Java中的hashcode方法

关于 hashCode() 你需要了解的 3 件事

java中hashcode()方法有什么作用呢?最好举个例子啊!


一、hashCode是什么

       有人发明了一种哈希算法来提高从集合中查找元素的效率,这种方式将集合分成若干个存储区域(可以看成一个个桶),每个对象可以计算出一个哈希码,可以根据哈希码分组分类,每组分别对应某个存储区域,这样一个对象根据它的哈希码就可以分到不同的存储区域(不同的桶中)。实际的使用中,一个对象一般有key和value,可以根据key来计算它的hashCode。如下图所示:

                      

       在Java的Object类中有一个方法:public native int hashCode(); 这是本地方法 ,因此在Object类中并没有给出具体的实现。


二、hashCode和equals方法的作用

       对于包含容器类型的程序设计语言来说,基本上都会涉及到hashCode。在Java中也一样,hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap以及HashTable。Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。有些朋友误以为默认情况下,hashCode返回的就是对象的存储地址。有些JVM在实现时是直接返回对象的存储地址,但是大多时候并不是这样,只能说可能存储地址有一定关联。

       考虑一种情况,当向集合中插入对象时,如何判别在集合中是否已经存在该对象了?(注意:集合中不允许重复的元素存在)可以用equals方法逐个进行比较,但是如果结合中已经存在大量数据,equals的效率是个问题。此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题。计算要查找对象的key的hashCode,然后找到该hashCode对应的存储区域,在该存储区域中来查找就可以了,这样效率也就提升了很多。这样经过一次计算,根据结果把查找范围缩小在一小片区域,调用equals方法的次数也大大降低,从而提高效率。


三、equals和hashCode的区别与联系

       equals和hashCode这两个方法都是用来判断两个对象是否相等的,但是他们是有区别的。

1.在用户调用方面

       一般来讲,equals这个方法是给用户调用的。如果你想判断两个对象是否相等,你可以重写equals方法,然后在代码中调用。简单来讲,equals方法主要是用来判断从表面上看或者从内容上看,两个对象是不是相等。举个例子,有个学生类,属性只有姓名和性别,那么我们可以认为只要姓名和性别相等,那么就说这两个对象是相等的。

       hashcode方法一般用户不会去调用。比如在hashmap中,由于key是不可以重复的,他在判断key是不是重复的时候就判断了hashcode这个方法,而且也用到了equals方法。这里的不可以重复表示equals和hashcode只要有一个不等就可以了!所以简单来讲,hashcode相当于是一个对象的编码,就好像文件中的md5,他和equals不同就在于他返回的是int型的,比较起来不直观。我们一般在覆盖equals的同时也要覆盖hashcode,让他们的逻辑一致。举个例子,还是刚刚的例子,如果姓名和性别相等就算两个对象相等的话,那么hashcode的方法也要返回姓名的hashcode值加上性别的hashcode值,这样从逻辑上,他们就一致了。
       要从物理上判断两个对象是否相等,用==就可以了。
2.在判断方面

       不能根据hashcode值判断两个对象是否相等,因为hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,不同的对象可能会生成相同的hashcode值。虽然不能根据hashcode值判断两个对象是否相等,但是可以直接根据hashcode值判断两个对象不等。如果要判断两个对象是否真正相等,必须通过equals方法。 

       要注意的是:如果调用equals方法得到的结果为true,则两个对象的hashcode值必定相等;如果equals方法得到的结果为false,则两个对象的hashcode值不一定不同;如果两个对象的hashcode值相等,则equals方法得到的结果未知。


四、使用的意事项

       1、一般来说涉及到对象之间的比较大小就需要重写equals方法。而重写equals方法,必须同时也重写hashCode方法,保持两者逻辑一致,反之亦然。一个类的对象如果没有存储在HashTable,HashSet,HashMap等散列存储结构中,可以不重写,但是有隐藏bug,日后存储到这些数据结构时很可能会出问题,例如数据不唯一性。而且在线性结构(如ArrayList)中是不会调用hashCode,所以重写了不影响性能。

       2、同一对象在执行期间若已经存储在集合中,则不能修改影响hashCode值的相关信息,否则会导致内存泄露问题。

       3、永远不要把哈希码误用作一个key,很容易出现不同对象而有相同值。

       4、一些类库中的类甚至指定它们用于计算哈希码的精确公式(例如字符串String类),这些类可以提供稳定的哈希值,但是有些类库在不同进程中会返回不同的哈希值,有的时候会让人困惑。

       5、在分布式应用中最好完全不使用哈希码可用SHA1代替用来标识对象。

       6、在分布式应用中不应该使用哈希码来持久化状态,或代码中不应该依赖于任何特定的哈希码值有可能这次打开程序与上次打开有不同的哈希吗,一个远程对象可能与本地对象有不同的哈希码,从一个版本到另一个版本哈希码的功能实现可能会更改导致产生不同的哈希吗。在哈希码的契约中,hashCode并不保证在不同的应用执行中得到相同的结果,java文档:在一次Java应用的执行中,对于同一个对象,hashCode方法必须始终返回相同的整数,但这整数不反映对象是否被修改(equals比较)的信息。同一个应用的不同执行,该整数不必保持一致。








你可能感兴趣的:(学习笔记之编程)