为什么hashCode和equals方法要一起重写?

我是针对在元素被存入哈希表时的流程分析的

默认实现

首先要明确,hashCode和equals这两个方法Object中的默认实现

  • hashCode方法
    @HotSpotIntrinsicCandidate
    public native int hashCode();

Object中,hashCode方法是被native修饰的,说明底层调用了c或c++的内容。其实做的事情是:根据对象实际的物理内存地址,通过某种算法得出一个数值。

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

equals方法的默认实现是:只有两个对象的保存的地址值是同一个时,才判断其相等,也就是说,两个引用指向堆内存中同一个对象,才判断相等。

数组、链表和哈希表

了解了hashCode和equals方法的默认实现了之后,我们再来看一看数组、链表和哈希表的特点

数组

数组的特点:查找快,增删慢
数组就像一列有序的格子,格子是连续的。
因为数组空间是连续的,而每个个元素所占的内存大小也是固定的,知道下标,就可以通过简单的数学表达式就可以计算出元素的内存地址,从而迅速定位到元素。
而增删慢的原因是:当想插入或删除某个元素到某个位置时,那么这个位置后面的每个元素的地址都要改变,还涉及到扩容问题。就像一排连续的被编好号的格子,每个格子里面都放了东西,现在你又添置了物品,想放在他们中间。
示例图

你能直接新加一个格子在他们中间,然后将新添置的物品放进去吗?这是不行的,因为之前的格子都被编好号了,所以是无法在他们之间新增一个格子,只能在最后加一个格子,接着最后的号排上号,然后挨个把物品往后挪,腾出前面的那个空格子,再将新购置的物品放进去。

链表

链表的特点,和数组相反。增删快,查找慢
链表就像一列人,手拉着手,每个人是一个节点。
链表的内存地址不是连续的,但是它的每个元素的记录了下一个元素的地址(单链表)。
增删快,因为如果要增加或者要删除或者要增加为一个元素,只需要将两个节点中保存的下一个节点修改一下就行了。就像一群手拉着手的小朋友在做游戏,然后新来了一个小朋友要加入他们,无论新加到那个位置,只需要让两个小朋友的手分开,然后把新来的那个小朋友放在他们中间,然后再重新让他们牵手,就行了,删除也是类似。
查找慢,因为链表查找,不能直接定位到内存地址,只能挨个遍历,然后比较是否相等。

哈希表

哈希表为什么性能高呢?因为它综合了数组和链表两者的有点,哈希表是由数组+链表/红黑树组成的,查找和增删的效率都很优秀。
链表的地址是就是通过哈希值计算得出的,如果哈希值相同,就将元素放在哈希值相等的一个链表/红黑树中。
哈希表存元素的步骤是:先通过hashCode方法得到这个元素的哈希值,再有这个哈希值计算得到一个内存地址值,然后再去看该内存地址上有没有元素,如果没有,就放在那里,如果已经有了,就去调用equals方法,看和已经存在的元素是否“相等”,如果相等,就舍去,如果不等,就将这个元素挂在已经存这个元素的链表(或红黑树)上

假设

现在来说为什么要同时重写hashCode和equals方法。

  • 假设不重写hashCode方法
    不重写hashCode方法,意味着,每次将元素存入哈希表时,哈希值都是通过Object类中默认的的hashCode方法得到的,因为默认的实现又是通过对象的地址计算得到的。所以每次每个对象得到的哈希值大概率不相同(我不知道是不是一定不同)。那么在通过哈希值计算数组的地址时,也全都是不同的地址,就这个哈希表就和数组没什区别了,不能发挥出哈希表的性能和优势。

  • 假设不重写equals方法
    这个得看hashCode方法有没有重写,如果hashCode方法没有重写,那么每次对象得到的哈希值不同,所以根本不会轮到equals方法出场,重写不重写都差不多。
    如果是重写了hashCode方法,那么在两个元素哈希值相同时,就会去调用Object中equals的默认实现,默认实现是啥?两个引用指向同一个对象时,才会判断相等。如果两个对象的内容是相同的话,也会被equals方法判断为不同,那么还是会被存入哈希表,而我们通常的认知是认为内容相同的对象就是相同的,不应该被存入哈希表。然而实际上却存入了,违背了哈希表不能存入重复元素的原则。

菜鸡一枚,没学过数据结构与算法,根据自己零零散散的经验总结的。如果说的有问题,还请指正。
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值