equals() 和 hashCode() 区别

转载自:https://www.cnblogs.com/jesonjason/p/5492208.html

equals():反映的是对象或变量具体的值,即两个对象里面包含的值--可能是对象的引用,也可能是值类型的值。

hashCode():计算出对象实例的哈希码,并返回哈希码,又称为散列函数。根类Object的hashCode()方法的计算依赖于对象实例的D(内存地址),故每个Object对象的hashCode都是唯一的;当然,当对象所对应的类重写了hashCode()方法时,结果就截然不同了。

之所以有hashCode方法,是因为在批量的对象比较中,hashCode要比equals来得快,很多集合都用到了hashCode,比如HashTable。

两个obj,如果equals()相等,hashCode()一定相等。

两个obj,如果hashCode()相等,equals()不一定相等(Hash散列值有冲突的情况,虽然概率很低)。

所以:在集合中,判断两个对象是否相等的规则是:

第一步,如果hashCode()相等,则查看第二步,否则不相等;

第二步,查看equals()是否相等,如果相等,则两obj相等,否则还是不相等。

为什么选择hashcode方法?

以java.lang.Object来理解,JVM每new一个Object,它都会将这个Object丢到一个 Hash 哈希表中去,这样的话,下次做 Object 的比较或者取这个对象的时候,它会根据对象的 hashcode 再从 Hash 表中取这个对象。这样做的目的是提高取对象的效率。具体过程是这样:

  1. new Object(),JVM根据这个对象的 Hashcode 值,放入到对应的Hash表对应的Key上,如果不同的对象确产生了相同的hash值,也就是发生了Hash key相同导致冲突的情况,那么就在这个Hash key的地方产生一个链表,将所有产生相同 hashcode 的对象放到这个单链表上去,串在一起。
  2. 比较两个对象的时候,首先根据他们的hashcode去hash表中找他的对象,当两个对象的hashcode相同,那么就是说他们这两个对象放在Hash表中的同一个key上,那么他们一定在这个key上的链表上。那么此时就只能根据Object的equal方法来比较这个对象是否equal。当两个对象的hashcode不同的话,肯定他们不能equal.

可能经过上面理论的讲一下大家都迷糊了,我也看了之后也是似懂非懂的。下面我举个例子详细说明下。

  list是可以重复的,set是不可以重复的。那么set存储数据的时候是怎样判断存进的数据是否已经存在。使用equals()方法呢,还是hashcode()方法。

​ 假如用 equals(),那么存储一个元素就要跟已存在的所有元素比较一遍,比如已存入100个元素,那么存101个元素的时候,就要调用equals方法100次。

  但如果用 hashcode() 方法的话,他就利用了 hash 算法来存储数据的。

  这样的话每存一个数据就调用一次 hashcode() 方法,得到一个 hashcode 值及存入的位置。如果该位置不存在数据那么就直接存入,否则调用一次 equals() 方法,不相同则存,相同不存。这样下来整个存储下来不需要调用几次 equals 方法,虽然多了几次 hashcode 方法,但相对于前面来讲效率高了不少

为什么要重写equals方法?

因为 Object 的 equal 方法默认是两个对象的引用的比较,意思就是指向同一内存,地址则相等,否则不相等;如果你现在需要利用对象里面的值来判断是否相等,则重载 equal 方法。

说道这个地方我相信很多人会有疑问,相信大家都被 String 对象的 equals() 方法和 " == " 纠结过一段时间,当时我们知道 String 对象中 equals 方法是判断值的,而==是地址判断。

那照这么说 equals 怎么会是地址的比较呢?

那是因为实际上JDK中,String、Math等封装类都对 Object 中的 equals() 方法进行了重写。

我们先看看Object中equals方法的源码:

public boolean equals(Object obj) { 
         return (this == obj); 
}

我们都知道所有的对象都拥有标识(内存地址)和状态(数据),同时“==”比较两个对象的的内存地址,所以说使用Object的 equals() 方法是比较两个对象的内存地址是否相等,即若object1.equals(object2) 为 true,则表示 equals1 和 equals2 实际上是引用同一个对象。虽然有时候 Object 的 equals() 方法可以满足我们一些基本的要求,但是我们必须要清楚我们很大部分时间都是进行两个对象的比较,这个时候 Object 的equals()方法就不可以了,所以才会有 String 这些类对 equals方法的改写,依次类推Double、Integer、Math。。。。等等这些类都是重写了 equals() 方法的,从而进行的是内容的比较。希望大家不要搞混了。

改写equals时总是要改写hashcode

java.lnag.Object 中对 hashCode的 约定:

  1. 在一个应用程序执行期间,如果一个对象的 equals 方法做比较所用到的信息没有被修改的话,则对该对象调用 hashCode 方法多次,它必须始终如一地返回同一个整数。
  2. 如果两个对象根据 equals(Object o) 方法是相等的,则调用这两个对象中任一对象的 hashCode 方法必须产生相同的整数结果。
  3. 如果两个对象根据 equals(Object o) 方法是不相等的,则调用这两个对象中任一个对象的 hashCode 方法,不要求产生不同的整数结果。但如果能不同,则可能提高散列表的性能。

根据上一个问题,实际上我们已经能很简单的解释这一点了,比如改写String中的equals为基于内容上的比较而不是内存地址的话,那么虽然 equals 相等,但并不代表内存地址相等,由 hashcode 方法的定义可知内存地址不同,没改写的 hashcode 值也可能不同。所以违背了第二条约定。

又如 new 一个对象,再 new 一个内容相等的对象,调用 equals 方法返回的 true,但他们的 hashcode 值不同,将两个对象存入 HashSet 中,会使得其中包含两个相等的对象,因为是先检索 hashcode 值,不等的情况下才会去比较 equals 方法的。

标签: java, equals hashcode

添加新评论