关于Object中equals方法和hashCode方法判断的分析
作者:Haozz_1994 发布时间:2023-12-23 19:41:53
首先提出这样一个问题:
如果两个对象不相同,他们的hashCode值一定不相等吗?
我们都知道equals和hashCode是Object中的方法,java中每一个对象都具有这两个方法。
equals(Object obj):判断两个对象是否“相同”,返回true或false;
public boolean equals(Object obj) {
return (this == obj);
}
hashCode():将该对象的内部地址作为一个int值返回
public native int hashCode();
再来看两条关于这两个方法的规范:
如果重写equals(Object obj)方法,有必要重写hashCode()方法,以确保equals(Object obj)方法返回为true的两个对象有相等的hashCode()返回值。也就是说,如果两个对象相同,他们的hashCode值应该相等;
如果equals(Object obj)方法返回false,即两个对象不“相同”,并不要求这两个对象调用hashCode()方法有不相等的返回值。也就是说,如果两个对象不相同,他们的hashCode可能相等。
当然,上述只是规范。针对规范1,如果重写equals(Object obj)返回true,而hashCode()方法返回不相等的值,也是可以编译过的。
这样我们可以作出如下推论:
如果两个对象equals,理论上讲他们的hashCode一定相等(至少Java环境会这样认为);
如果两个对象不equals,他们的hashCode有可能相等;
如果两个对象hashCode相等,他们不一定equals;
如果两个对象hashCode不相等,他们一定不equals。
看着有点绕,其实原理很简单。我们从推论3和推论4可以预测:
Java在判断两个对象是否“相同”时,首先判断他们的hashCode()方法是否返回相等的int值,其次判断equals方法是否返回true。
我们可以写一段简单的代码测试一下:
首先写一个Java类:
public class Person {
//重写equals方法,始终返回false;
@Override
public boolean equals(Object obj) {
System.out.println("判断Person的equals");
return false;
}
//重写hashCode方法,始终返回1;
@Override
public int hashCode() {
System.out.println("判断Person的hashCode");
return 1;
}
}
上述代码中Person类重写了equals方法,打印并始终返回false,重写了hashCode方法,打印并始终返回1。
我们都知道Map中要求键不能重复,也就是不能“相同”,所以可以写如下的测试类:
public class TestPerson {
@Test
public void test(){
Map<Person,Object> map = new HashMap<>();
map.put(new Person(),new Object());//放入第1个Person-Object键值对;
System.out.println("=====================");
map.put(new Person(),new Object());//放入第2个Person-Object键值对;
System.out.println(map.size());
}
}
运行,打印结果如下
判断Person的hashCode
=====================
判断Person的hashCode
判断Person的equals
2
我们来分析一下:
当放入第1个Person-Object键值对时,Java会判断map中有没有和当前添加的new Person()相同的对象,于是去调用了Person的hashCode()方法,得到返回值1,发现此时map中没有相等的hashCode为1的Person对象(因为此时map为空),所以不再判断equals方法,将这个键值对放入map中;(推论4:如果两个对象hashCode不相等,他们一定不equals)
当放入第2个Person-Object键值对时,Java依然采用相同的判断方式,hashCode()方法判断之后得到返回值为1,发现此时map中有相等的hashCode值的Person对象,然后再去判断equals方法,得到返回值false(推论3:如果两个对象的hashCode相等,他们不一定equals),认为这两个对象不相同,于是将第2个键值对也放入map中。执行之后得到map的size为2
所以可以得出结论:
Java在判断两个对象是否“相同”时,首先判断他们的hashCode()方法是否返回相等的int值,如果不相等则直接认为他们不“相同”,如果相等,再判断equals方法是否返回true。
针对上述代码,可以在equals方法和hashCode方法中分别打断点,Debug运行,这样会看得比较清楚一点。
我们回到最初的那个问题:如果两个对象不相同,他们的hashCode值一定不相等吗?
上述代码中的场景就充分说明两个对象不相同时hashCode值却相等的场景,当然,这是不按照规范操作的情况。所以写代码时一定要按照规范要求的去做,避免不必要的BUG
可以试想一下,如果将上述代码中重写equals方法中的始终返回false改为始终返回true,又会是怎样的结果。
来源:https://blog.csdn.net/hz_940611/article/details/80365983
猜你喜欢
- 本文实例讲述了Java后台线程操作。分享给大家供大家参考,具体如下:一 点睛有一种线程,它是后面运行的,它的任务是为其他线程提供服务,这种线
- 1. SpEL 回顾经过上篇文章的学习,小伙伴们已经知道了,在 Spring Security 中,@PreAuthorize、@PostA
- @Value("${xxx}")取properties时中文乱码(1)检查spring的配置文件中,properties
- 本文实例为大家分享了Android实现屏幕录制功能的具体代码,供大家参考,具体内容如下1.效果图:2.添加依赖 dependenc
- 一、代码实现创建窗口首先创建一个游戏窗体类GameFrame,继承至JFrame,用来显示在屏幕上(window的对象),每个游戏都有一个窗
- 到底什么是反射呢???反射的核心就是JVM在运行时才动态加载类或调用方法,访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。每
- 我们很多时候会碰到这样的问题,使用多线程刷一个表的数据时需要多个线程不能重复提取数据,那么这个时候就需要使用到线程的排他锁了。在c#里面其实
- 对Jpa Entity关系映射中mappedBy的理解mappedBy 单向关系不需要设置该属性,双向关系必须设置,避免双方都建立外键字段数
- 大家对于 Spring 的 scope 应该都不会默认。所谓 scope,字面理解就是“作用域”、“范围”,如果一个 bean 的 scop
- 引言在第一篇文章中我们分析了协程启动创建过程启动过程,在本文中,我们将着重剖析协程中协程调度的逻辑流程。主要是分析解答如下2个问题:涉及到协
- 异常日志[com.alibaba.dubbo.rpc.filter.TimeoutFilter] - [DUBBO] invok
- 一直以来不是怎么清楚自旋锁,最近有点时间,好好的学习了一下;所谓的自旋锁在我的理解就是多个线程在尝试获取锁的时候,其中一个线程获取锁之后,其
- 说明:1、集合类型参数化;2、可根据集合中的对象的各个属性进行排序,传入属性名称即可;注:属性必须实现了IComparable接口,C#中i
- 原来一直使用shiro做安全框架,配置起来相当方便,正好有机会接触下SpringSecurity,学习下这个。顺道结合下jwt,把安全信息管
- C# 程序的通用结构C# 程序可由一个或多个文件组成。每个文件都可以包含零个或零个以上的命名空间。一个命名空间除了可包含其他命名空间外,还可
- 今天给大家带来一个自定义的底部导航,我不会做动图,只能搞一张图片给大家看看,大家见谅这个就是自定义的tablayout底部搞好的样式了Tab
- Selenium 是目前用的最广泛的Web UI 自动化测试框架。 本系列文章,将深入简出来讲解selenium 的用法阅读目录seleni
- Android 加载大图及多图避免程序出现OOM(OutOfMemory)异常1、高效加载大图片我们在编写Android程序的时候经常要用到
- 目录1.引用Nuget包 ServiceStack.Redis2. string 类型的使用作
- AOP事务管理<aop:advisor>两种配置方式方式一@transactionManagerbean.xml<?xml