“==”、“equals()”、“hashcode()”之间的秘密
前言
万丈高楼平地起,今天的聊点基础而又经常让人忽视的话题,比如“==”与“equals()”区别?为何当我们重写完"equals()"后也要有必要去重写"hashcode()"呢? ... 带着这些问题,我们一起来探究一下。
概念
"==":它主要是判断符号两边的“对象”的值是否相等,而这里的“值“”又有所区分了。
-
基础数据类型:比较的就是自身的值,这个跟我们常规的理解是基本一致的。
-
引用数据类型:比较的对象的内存地址。
“equals()”:它也是用来判断两个对象是否相等,所以也得分不同的情况来说明。
-
在当前类中,没有重写equals方法的话,默认的实现跟"=="的实现是一样的。下面是Object类的equals方法实现。
-
在当前类中,重写了equals方法,此时判断的依据就是你重写的逻辑。
怎样重写equals()方法?
1、自反性:对于任何非空引用x,x.equals(x)应该返回true。
2、对称性:对于任何引用x和y,如果x.equals(y)返回true,那么y.equals(x)也应该返回true。
3、传递性:对于任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也应该返回true。
4、一致性:如果x和y引用的对象没有发生变化,那么反复调用x.equals(y)应该返回同样的结果。
5、非空性:对于任意非空引用x,x.equals(null)应该返回false。
由此可以看出,重写一个equals()方法,需要注意的点还是比较多的,这里给出一个参考的事例。
public class EqualsDemo { private String name; private String info; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EqualsDemo that = (EqualsDemo) o; if (name != null ? !name.equals(that.name) : that.name != null) return false; return info != null ? info.equals(that.info) : that.info == null; } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + (info != null ? info.hashCode() : 0); return result; } }
有些读者可能会感到奇怪,不是说重写equals()方法吗,为什么这里又出现了一个hashcode()?所以这里又引出了我们的另一个主角hashcode()方法,当我们重写了equals()方法后,它就一定会出现,也会“吵着“自己也要被重写。
什么是hashcode()?
hashCode() 的作用是获取哈希码,也称为散列码;它返回的一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap、HashTable等。它定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。
当我们在上面的集合插入对象的时候,java是怎么知道里面是否有重复的对象呢?可能大家第一反应是equals方法,没错这方法可以实现这个功能,但是当集合里面有成千上万个元素的时候,效率会如何呢?答案当然是比较差了,所以才会出现了哈希码。
public V put(K key, V value) { //判断当前数组是否等于{},若是则初始化数组 if (table == EMPTY_TABLE) { inflateTable(threshold); } //判断 key 是否等于 null,是则将把当前键值对添加进table[0]中,遍历table[0]链表 //如果已经有null为key的Entry,则修改值,返回旧值,若无则直接添加。 if (key == null) return putForNullKey(value); //key不为null则计算hash int hash = hash(key); //搜索对应hash所在的table中的索引 int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } //修改次数 modCount++; addEntry(hash, key, value, i); return null; }
这里是jdk7中 Hashmap put()方法的实现,通过源码的注释可以看出执行的流程,需要更详细的了解HashMap可以参考我之前发在开源中国的博客《Java7 HashMap全面解读! 》,链接:https://my.oschina.net/19921228/blog/752073
经过概念的介绍,知道为什么重写完equals()后要接着重写hashcode()了吧?
People p1=new People("小明",18);
People p2=new People("小明",18);
此时重写了equals方法,p1.equals(p2)一定返回true,假如只重写equals而不重写hashcode,那么Student类的hashcode方法就是Object默认的hashcode方法,由于默认的hashcode方法是根据对象的内存地址经哈希算法得来的,显然此时s1!=s2,故两者的hashcode不一定相等。所以在一些集合的使用当中会出现问题。
总结
小小的几个方法,没想到却有这么多“坑”,而且在面试中也会经常被问到,在金三银四的时候,但愿各位不会陷在这里。
喜欢的话,劳烦点个赞,关注一下<深夜里的程序猿>噢
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
一条sql语句在mysql中是如何执行的
概览 最近开始在学习mysql相关知识,自己根据学到的知识点,根据自己的理解整理分享出来,本篇文章会分析下一个sql语句在mysql中的执行流程,包括sql的查询在mysql内部会怎么流转,sql语句的更新是怎么完成的。 一、mysql架构分析 下面是mysql的一个简要架构图: mysql主要分为Server层和存储引擎层 Server层:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binglog日志模块。 存储引擎: 主要负责数据的存储和读取,采用可以替换的插件式架构,支持InnoDB、MyISAM、Memory等多个存储引擎,其中InnoDB引擎有自有的日志模块redolog 模块。 InnoDB 5.5.5版本作为默认引擎。 连接器 主要负责用户登录数据库,进行用户的身份认证,包括校验账户密码,权限等操作,如果用户账户密码已通过,连接器会到权限表中查询该用户的所有权限,之后在这个连接里的权限逻辑判断都是会依赖此时读取到的权限数据,也就是说,后续只要这个连接不断开,即时管理员修...
- 下一篇
如果再有人问你分布式 ID,这篇文章丢给他
1.背景 在我们的业务需求中通常有需要一些唯一的ID,来记录我们某个数据的标识: 某个用户的ID 某个订单的单号 某个信息的ID 通常我们会调研各种各样的生成策略,根据不同的业务,采取最合适的策略,下面我会讨论一下各种策略/算法,以及他们的一些优劣点。 2.UUID UUID是通用唯一识别码(Universally Unique Identifier)的缩写,开放软件基金会(OSF)规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素。利用这些元素来生成UUID。 UUID是由128位二进制组成,一般转换成十六进制,然后用String表示。在java中有个UUID类,在他的注释中我们看见这里有4种不同的UUID的生成策略: randomly: 基于随机数生成UUID,由于Java中的随机数是伪随机数,其重复的概率是可以被计算出来的。这个一般我们用下面的代码获取基于随机数的UUID: time-based:基于时间的UUID,这个一般是通过当前时间,随机数,和本地Mac地址来计算出来,自带的JDK包并没有这个算法的我们在一些UUIDUtil中,...
相关文章
文章评论
共有0条评论来说两句吧...