【Java基础】之深入讨论equals()和hashcode()方法
这篇文章将详解介绍 Java 的 equals() 与 hashCode() 方法 |
我们知道 Object 类是类继承结构的基础,所以是每一个类的父类,所有的对象,包括数组,都实现了在 Object 类中定义的方法。而在 Object 类中有两个非常重要的方法:
public boolean equals(Object obj)
public int hashCode()
这两个方法是息息相关、紧密相连的,下面我们就来详细介绍一下这两个方法的作用。
equals() 方法
equals() 方法是用来判断其他的对象和该对象是否相等。在 Object 类中 equals() 方法的定义如下:
public boolean equals(Object obj) { return (this == obj);
}
很明显这是对两个对象的地址值进行的比较(即比较引用是否相同),但是我们知道,String 、Math、Integer、Double 等这些封装类在使用 equals() 方法时,已经覆盖了 object 类的 equals() 方法,比如 String 类中的 equals() 如下:
// String 的 equals 方法
public boolean equals(Object anObject) {if (this == anObject) {return true;}if (anObject instanceof String) {String aString = (String)anObject;if (!COMPACT_STRINGS || this.coder == aString.coder) {// 调用 StringLatin1.equals() 方法return StringLatin1.equals(value, aString.value);}}return false;
}// StringLatin1.equals() 方法
public static boolean equals(byte[] value, byte[] other) {if (value.length == other.length) {for (int i = 0; i < value.length; i++) {if (value[i] != other[i]) {return false;}}return true;}return false;
}
很明显,这是进行的内容比较,而已经不再是简单的地址比较了。类似的 Math、Integer、Double 等这些类都是重写了 equals() 方法的,从而进行的是内容的比较,当然,基本类型是进行值的比较。
需要注意的是当 equals() 方法被 override 时,hashCode() 也要被 override,按照一般 hashCode() 方法的实现来说,相等的对象,它们的 hash code 一定相等。
hashcode() 方法
hashCode() 方法给对象返回一个 hash code 值,这个方法用于哈希表(hash tables),例如 HashMap。
hashCode() 需要遵循一下规则:
- 在一个 Java 应用的执行期间,如果一个对象提供给 equals 做比较的信息没有被修改的话,该对象多次调用 hashCode() 方法时,必须返回同一个值。
- 如果两个对象根据 equals(Object) 方法是相等的,那么调用二者各自的 hashCode() 方法产生的值必须相等
- 不相等的两个对象,其 hashCode() 得到的值可以相等也可以不相等,但程序设计时应该尽量让不同对象产生不同的值,这可以提高哈希表的性能
大量的实践表明,由 Object 类定义的 hashCode() 方法对于不同的对象返回不同的值,在 Object 类中,hashCode() 定义如下:
public native int hashCode();
这是一个本地(native)方法,它的实现是和本地机器相关的。当然我们也可以覆盖 hashcode() 方法,比如 String、Integer、Double 等这些类都是覆盖了 hashcode() 方法的。例如在 String 类中定义的 hashcode() 方法如下:
// String 的 hashCode() 方法定义
public int hashCode() { int h = hash;if (h == 0 && !hashIsZero) {h = isLatin1() ? StringLatin1.hashCode(value): StringUTF16.hashCode(value);if (h == 0) {hashIsZero = true;} else {hash = h;}}return h;
}// StringLatin1.hashCode() 方法定义
public static int hashCode(byte[] value) {int h = 0;for (byte v : value) {h = 31 * h + (v & 0xff);}return h;
}
想要弄明白 hashCode 的作用,必须要先知道 Java 中的集合:
Java 中的集合(Collection)有两类:一类是 List,再有一类是 Set。前者集合内的元素是有序的,元素可以重复,后者元素无序,但元素不可重复。
这里就引出一个问题:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?
判断依据就是 Object.equals() 方法。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次 equals 方法,这显然会大大降低效率。
于是,Java 采用了哈希表的原理,哈希(Hash)实际上是个人名,由于他提出了哈希算法的概念,所以就以他的名字命名。哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上,可以简单理解为:hashCode 方法实际上返回的就是对象存储的物理地址(实际可能并不是)。
这样一来,当集合要添加新的元素时,先调用这个元素的 hashCode 方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了。如果这个位置上已经有元素了,就调用它的 equals 方法与新元素进行比较,相同的话就不用保存了,不相同的就散列在其它的地址,所以这里存在一个冲突解决的问题。但这样一来,实际调用 equals 方法的次数就大大降低了,几乎只需要一两次就可以了。
总的来说就是:hashcode 能大大降低对象比较次数,提高查找效率。
equals() 和 hashcode()
Java 对象的 eqauls 方法和 hashCode 方法有以下两点规定:
相等或相同的对象必须具有相等的哈希码
想象一下,假如两个 Java 对象 A 和 B 相等(eqauls 结果为 true),但 A 和 B 的哈希码不同,则 A 和 B 存入 HashMap 时的哈希码计算得到的 HashMap 内部数组位置索引就可能不同,那么 A 和 B 很有可能允许同时存入 HashMap,显然相等或相同的元素是不允许同时存入 HashMap 的,因为 HashMap 不允许存放重复元素。
如果两个对象的 hashCode 相同,它们并不一定相同
也就是说,不同对象的 hashCode 可能相同。我们举个例子,假如两个 Java 对象 A 和 B 不相等(eqauls 结果为 false),但 A 和 B 的哈希码相等,当将 A 和 B 都存入 HashMap 时就会发生哈希冲突,也就是 A 和 B 存放在 HashMap 内部数组的位置索引相同,这时 HashMap 会在该位置建立一个链接表,将 A 和 B 串起来放在该位置,显然,该情况不违反 HashMap 的使用原则,是允许的(当然,哈希冲突越少越好,尽量采用好的哈希算法以避免哈希冲突)。
所以,Java 对于 eqauls 方法和 hashCode 方法有这样的规定:
- 如果两个对象相同,那么它们的 hashCode 值一定要相同
- 如果两个对象的 hashCode 相同,它们并不一定相同(这里说的对象相同指的是用 eqauls 方法比较)
总结就是:equals() 相等的两个对象,hashcode()一定相等;equals() 不相等的两个对象,hashcode() 可能相等,也可能不等。反过来,hashcode() 不相同,equals() 一定也不等,但 hashcode() 相等,equals() 可能相等,也可能不等
哈希集合与 hashcode()、equals() 的关系
与哈希集合有关的集合有:Hashset、Hashmap、Hashtable等等,它们的存储操作是根据什么原理来存取对象的呢?
下面以 HashSet 为例进行分析,我们都知道:在 hashset 中不允许出现重复对象,元素的位置也是不确定的,那 hashset 中又是怎样判定元素是否重复的呢?
在 java 的集合中,判断两个对象是否相等的规则是:
判断两个对象的 hashCode 是否相等,如果不相等,则认为两个对象也不相等,结束判断。如果 hashCode 不相同,那么就用 equals 判断两个对象是否相等,如果不相等,则认为两个对象也不相等,如果相等则认为两个对象相等(equals()是判断两个对象是否相等的关键)
举个例子:
public class HashSetTest {public static void main(String args[]) {String s1 = new String("hello");String s2 = new String("hello");System.out.println("s1 == s2 : " + s1 == s2);System.out.println("s1.equals(s2) : " + s1.equals(s2));System.out.println("s1.hashCode() : " + s1.hashCode());System.out.println("s2.hashCode() : " + s2.hashCode());Set hashset = new HashSet();hashset.add(s1);hashset.add(s2);Iterator it = hashset.iterator();while (it.hasNext()) {System.out.println(it.next());}}
}>>>>>
s1 == s2 : false
s1.equals(s2) : true
s1.hashCode() : 87652
s2.hashCode() : 87652
hello
这个例子中,由于 String 类已经重写了 equals() 和 hashcode() 方法(比较的是字符串内容),所以 hashset 认为它们是相等的对象,而没有重复添加进去,所以迭代的时候只输出一个字符串内容。
所以我们可以总结出:如果自己定义的类中如果没有重写 equals() 和 hashcode() 方法,那么添加对象到集合里时就会调用父类 Object 的 equals() 和 hashcode() 方法。而 Object 类中的 hashcode() 方法是一个本地方法,比较的是对象的地址(引用地址),所以使用 new 方法创建的对象当然是不同的对象了,造成的结果就是两个对象的 hashcode() 返回的值不一样,这样会导致哈希集合会把它们当作不同的对象对待。
为了避免这种情况,我们建议:自定义类时应该重写 equals() 和 hashcode() 方法
【Java基础】之深入讨论equals()和hashcode()方法相关推荐
- Java基础提升篇:equals()与hashCode()方法详解
概述 java.lang.Object类中有两个非常重要的方法: public boolean equals(Object obj) public int hashCode() Object类是类继承 ...
- 【JAVA基础篇】==、equals和hashCode的区别和联系
== 作用:比较两个操作数的关系,返回一个boolean类型的结果 具体含义:如果两个操作数是基本数据类型,比较值是否相等.如果两个操作数是引用类型,那么比较的是内存地址是否相同. equals Ob ...
- Java拾遗:001 - 重写 equals 和 hashCode 方法
2019独角兽企业重金招聘Python工程师标准>>> 重写equals方法 在Java中Object类是一个具体类,但它设计的主要目的是为了扩展,所以它的所有非final方法,都被 ...
- 搞懂 Java equals 和 hashCode 方法
搞懂 Java equals 和 hashCode 方法 分析完 Java List 容器的源码后,本来想直接进入 Set 和 Map 容器的源码分析,但是对于这两种容器,内部存储元素的方式的都是以键 ...
- Java基础-Integer的==和equals方法
Java基础-Integer的==和equals方法 1.首先说下 equals 方法: equals 方法接受的参数为 Object 类型 equals(Object obj),首先会判断参数中 ...
- java baseentity_如何在JPA的BaseEntity中实现equals()和hashcode()方法?
我有一个BaseEntity类,它是我的应用程序中所有JPA实体的超类. @MappedSuperclass public abstract class BaseEntity implements S ...
- List去重为什么要写equals(),hashCode()方法
一,各个集合的特点: Collection(集合):容器,用于存放对象(引用类型.基本类型需要自动装箱) List(列表):元素有序,元素可以重复 (有索引). 通过元素的equals()方法判断是否 ...
- 深入理解Java的equals和hashCode方法
1.谈谈equals方法 相信大家对这个这个方法一定不陌生.该方法是Object基类里的非final方法(被设计成可覆盖的),下面我们来看看Object中是如何实现该方法的.源代码如下: public ...
- 学习Java编程equals()和hashCode()方法
equals()和hashCode()区别? equals():反映的是对象或变量具体的值,即两个对象里面包含的值--可能是对象的引用,也可能是值类型的值. hashCode():计算出对象实例的哈希 ...
最新文章
- spring boot validated的使用
- Reverse Linked List
- 算算是第几天的C语言程序,计算当日是该年的第几天---C语言
- github issue 搜索_回顾 2020 年 GitHub 的大事件,你知道多少?
- 阿里云服务器ECS和腾讯云服务器如何安装宝塔面板?
- 关于安装CNPM 与搭建VUE空白项目
- es6异步编程 Promise 讲解 --------各个优点缺点总结
- vue项目运用繁体字
- python 根据x的值和函数y=20+x2,计算y_new,算出y_new和y的差,记为delta_y。¶绘制x和delt_y的点图,并计算y的方差。有关方差的计算参阅数学资料。
- 微信文件下载内容如何调整存储位置?
- linux查看hive账户权限,Linux用户和权限管理
- 后台管理系统模板简介
- 域格EVB开发板使用说明
- 正大国际期货:做期货主帐户有什么风险?
- 视频画面滚动字幕怎么做,让你几分钟学会的方法
- html中鱼眼效果,鱼眼效果和放大效果怎么做
- 喜闻乐见之Activity生命周期
- jenkins远程执行脚本不退出Exec in pty
- CAD转JPG,在线转换,没有转换器
- ABB AC500 系列 PLC 与上位机iFix 的通讯配置
热门文章
- 【Java】iText POI
- oracle中ccuser,Oracle数据库查询与SESSIONS_PER_USER大于40
- linux显示器镜像翻转,Ubuntu Linux显示器屏幕偏移的解决办法
- boost:chrono
- 装ghost遇到的问题之一“Windows failed to start. A Recent hardware or software change might be the cause.”
- Word文件删除后怎么恢复?好用的恢复方法分享
- STL算法——常用查找算法(find、find_if、adjacent_find、binary_search、count、count_if)
- Path环境变量的配置
- TextView的android:maxHeight,android:minHeight的正确设置
- 关于微信小程序form表单无法触发提交的解决方案