彻底搞懂为什么重写equals还要重写hashcode?

引言

原文链接:深入理解equals和hashCode 由于hashCode与HashMap有一定关系,推荐大家看一下我的这篇文章 HashMap源码大剖析 

彻底搞懂为什么重写equals还要重写hashcode?_第1张图片

本文介绍java.lang.Object类中的两个方法:equals和hashCode。这两个方法大家应该都知道,但是这两个方法的作用是什么、为什么重写equals还要重写hashCode、它们之间有什么关系和约定等,或许有些小伙伴还不是很清楚,知其然知其所以然,今天就来带大家了解一下。

百度搜索"为什么重写equals一定要重写hashcode",第二条结果就是我写的文章。这是我很久之前写的,其中的内容表述得不太清楚,或多或少难以让人信服,于是我决定再写一篇。

彻底搞懂为什么重写equals还要重写hashcode?_第2张图片

1、hashCode

在阅读下面的内容之前,大家可能需要先了解一下什么是哈希表、哈希函数,这是数据结构相关的知识。

hashCode即散列码。散列码是用一个int值来代表对象,它是通过将该对象的某些信息进行转换而生成的。

Object类中默认的hashCode方法如下。

public native int hashCode();

这是一个本地方法,不同的虚拟机有不同的实现(具体实现自己看虚拟机源码哈)。Object默认的hashCode是根据对象的内存地址转化而来的,它是唯一的。

我们可以在自己的类中覆盖hashCode方法,但我们可以使用System.identityHashCode(Object x)方法返回默认的hashcode,无论对象是否覆盖默认的hashcode。

hashCode方法主要是为了给诸如HashMap这样的哈希表使用

设计hashcode最重要的因素是:对同一个对象调用hachCode()应该产生同样的值(前提是对象的信息没有被改变)。

设计一个hashCode,它必须快,而且具有意义(使用有意义的字段来生成hashcode)。hashCode不需要唯一(默认的hashCode唯一),因此更应该关注它的速度,而不是唯一性

由于在生成桶(桶指哈希桶,或哈希表的槽位)的下标前,hashcode还要做进一步处理,所以生成的hashCode范围不是很重要,是int就行。

好的hashCode()应该产生分布均匀的散列码。

哈希桶的大小最好是2的n次方。

  • 对现代处理器来说,除数和求余是最慢的操作,而使用2的n次方,可以用位运算代替求余(%开销较大)。

  • 举个例子,假设哈希桶大小为16(HashMap初始大小),假设hashCode为20,那么使用%求余会得到下标4;但这可以用hashCode&(length-1)代替,即20&(16-1),结果也是4。

2、equals

hashCode并不需要唯一性,但equals必须严格地判断两个对象是否相同。

正确的equals方法有如下特性:

  • 自反性:x.equals(x)一定返回true

  • 对称性:如果x.equals(y)为true,那么y.equals(x)也为true

  • 传递性:如果x.equals(y)为true、y.equals(z)为true,那么x.equals(z)也为true

  • 一致性:如果x和y中用于等价比较的信息没有改变,那么x.equals(y)无论调用多少次,结果都一致

  • 任何不是null的x,x.equals(null)一定返回false

3、equals与hashCode的相关规定

之所以有规定,是为了使诸如HashMap这样的哈希表正常使用。具体规定如下:

  1. equals相等,hashcode一定相等。

  2. equals不等,hashcode不一定不等。

  3. hashcode不等,equals一定不等。

  4. hashcode相等,equals不一定相等。

因此,如果我们重写了equals,那么必须重写hashCode,使其满足这些规定。当然,如果我们不把自定义对象当成HashMap的键来使用,那么自定义对象不重写equals和hashCode也是可以的。

下面来详细解释一下,为什么这些规定能让HashMap正常工作。

3.1、equals相等,hashCode一定相等

因为HashMap是用equals判断键是否相等的,用反证法,如果两个键 equals相等,而hashcode不等的话,那么就无法保证通过hashcode计算的下标值相等,下标值不等也就意味着相等的两个键却 到了不同的值,这肯定是不对的。

3.2、equals不等,hashcode不一定不等

equals不等,一般hashcode也不相等,这是为了尽量减少哈希冲突。但为啥会出现相等的情况呢,因为hashcode是int类型,是有范围的,当数据量很大的情况下,难免会发生冲突,此时HashMap通过拉链法解决冲突。

当从map中获取equals不等的两个键时,由于它们的hashcode相同,所以计算到的下标值也相同,当定位到同一个桶位时,会在单链表上顺序查找,查找到的依据就是要查找的键与单链表上的键equals相等。

3.3、hashcode不等,equals一定不等

这个相当于规定1的逆反命题。

3.4、hashcode相等,equals不一定相等

这个相当于规定2的逆反命题。如果两个键hashcode相等,那么计算到的下标值是相同的,这时候两个键可能是相等的(该桶位有一个元素),也可能是不等的(该桶位有两个元素)。

你可能感兴趣的:(Java生态,equals,hashcode,为什么重写equals,还要重写hashcode,hashmap)