Java equals()方法和hashCode()方法
equals()方法
如果满足了以下任何一个条件,就不需要覆盖equals()方法:
1 类的每个实例本质上都是唯一的。
2 不关心类是否提供了“逻辑相等”的测试功能。
3 父类已经覆盖了equals,从父类继承过来的行为对于子类也是合适的。
4 类是私有的或是包内私有的,可以确定它的equals方法永远不会被调用。
如果类具有自己特有的“逻辑相等”概念(不同于对象等同的概念),而且父类没有覆盖equals()方法以实现期望的行为,就需要覆盖equals()方法。这通常属于“值类”的情形。值类仅仅是一个表示值的类,例如Integer或者Date。在用equals()方法比较值对象的引用时,希望知道它们在逻辑上是否相等,而不是想了解它们是否指向同一个对象。
单例类不需要覆盖equals()方法。因为对于这样的类,逻辑相同与对象等同是一回事,Object的equals()方法等同于逻辑意义上的equals()方法。
覆盖equals()方法的通用约定如下:
1 自反性
对于任何非null的引用x,x.equals(x)必须返回true。
2 对称性
对于任何非null的引用x和y,仅当x.equals(y)返回true时,y.equals(x)必须返回true。
3 传递性
对于任何非null的引用x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true。
4 一致性
对于任何非null的引用x和y,如果比较的两个对象没有被修改,那么多次调用x.equals(y)就会一致地返回true,或者一致地返回false。
5 非空性
对于任何非null的引用x,x.equals(null)必须返回false。
实现equals()方法的步骤如下:
1 ==
使用==操作符检查“参数是否为这个对象的引用”,即通过比较地址来判断两个对象是否等同。
2 instanceof
使用instanceof操作符检查“参数是否为正确的类型”,即通过instanceof操作符来判断比较的是否为同一个类的对象。
3 强制类型转换
把参数转换成正确的类型,即把参数Object对象强制转换成当前类的对象。
4 匹配“关键”域
对于该类中的每个“关键”域,检查参数中的域是否与该对象中对应的域相匹配,即判断两个对象包含的内容是否相同。
对于既不是float也不是double类型的基本类型域,可以使用==操作符进行比较;对于对象引用域,可以递归地调用equals()方法;对于float域,可以使用Float.compare()方法;对于double域,则使用Double.compare()方法。对float和double域进行特殊的处理,是因为存在Float.NaN、-0.0f以及类似的double常量。对于数组域,则要把以上这些规则应用到每个元素上。如果数组域中的每个元素都很重要,就可以使用发行版本1.5中新增的其中一个Arrays.equals()方法。
注意点如下:
1 覆盖equals时总要覆盖hashCode。
2 加上@Override注解,不要将参数Object对象替换为其他类型,因为替换之后方法不是重写,而是重载。
String中的equals()方法如下:
// 比较内容
public boolean equals(Object anObject) {
// 如果对象等同
if (this == anObject) {
return true;
} // 如果比较的是String对象
if (anObject instanceof String) {
// 强制类型转换
String anotherString = (String)anObject;
// 获取当前String对象中的字符数组长度
int n = value.length;
// 如果两个字符串长度相等
// 否则,返回false
if (n == anotherString.value.length) {
// 定义字符数组指针
char v1[] = value;
char v2[] = anotherString.value; // 依次比较等长部分的字符
int i = 0;
while (n-- != 0) {
// 如果字符不同,则返回false
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
hashCode()方法
Object的hashCode()是一个本地方法。
public native int hashCode();
在每个覆盖了equals()方法的类中,必须覆盖hashCode()方法,即覆盖equals时总要覆盖hashCode。
覆盖hashCode()方法的通用约定如下:
1 同一个对象调用hashCode()方法返回同一个整数。
2 如果equals()方法返回true,那么hashCode()方法返回的整数相等。
3 如果equals()方法返回false,那么hashCode()方法返回的整数不一定不相等。但是应该知道,对于不相等的对象,产生不同的整数结果,有助于提高散列表的性能。
如果不满足第1条,HashMap无法查找到插入的键值对。覆盖equals时没有覆盖hashCode而违反的关键约定是第2条:相等对象具有相等的散列码(hash code)。其中,相等对象指相同内容的不同对象。如果不覆盖hashCode()方法,相等对象调用hashCode()方法返回的整数不一定相等。当散列码不相等时,相等对象可能会被插入到不能包含相等对象作为key的集合(例如HashMap)中。与第2条互为逆否命题的是,如果hashCode()方法返回的整数不相等,那么equals()方法返回false。比较散列码有利于减少equals()方法的调用次数,提高效率。另外,如果hashCode()方法返回的整数相等,那么equals()方法不一定返回true。
一个好的散列函数为不相等的对象产生不相等的散列码。理想情况下,散列函数应该把集合中不相等的实例均匀地分布到所有可能的散列值上。解决方法如下:
1 把某个非0的常数值(例如17)保存在一个名为result的int类型变量中。
2 对于equals()方法涉及的每个域,完成如下步骤:
2.1 计算该域的散列码c。
2.1.1 如果该域是boolean类型,则计算(f ? 1 : 0)。
2.1.2 如果该域是byte、char、short或者int类型,则计算(int)f。
2.1.3 如果该域是long类型,则计算(int)(f ^ (f >>> 32))。
2.1.4 如果该域是float类型,则计算Float.floatToIntBits(f)。
2.1.5 如果该域是double类型,则计算Double.doubleToLongBits(f),然后按照步骤2.1.3继续计算。
2.1.6 如果该域是一个对象引用
2.1.6.1 如果递归调用equals()方法来比较该域,则递归调用该域的hashCode()方法。
2.1.6.2 否则,自行确定规则,计算该域的散列码。其中,如果该引用值为null,则返回0。
2.1.7 如果该域是一个数组,则把每个重要的元素通过上述规则来计算,并根据2.2把这些散列码合并到result中。如果数组中的每个元素都重要,则可以调用JDK 1.5新增的Arrays.hashCode()方法。
2.2 根据result = 31 * result + c;(31 * result会被JVM优化成(result << 5) - result),把之前得到的散列码c合并到result中。(31是奇素数,如果乘数是偶数,并且出现乘法溢出,则信息就会丢失,因为与2相乘等价于位运算向左移一位。使用素数的好处不很明显,但是习惯上使用素数来计算散列码。)
3 返回result。
在String中,只是字母顺序不同的所有字符串具有相等的散列码,代码如下:
// 获取散列码
public int hashCode() {
// 获取字符串缓存散列码
int h = hash;
// 如果需要计算散列码
if (h == 0 && value.length > 0) {
// 定义字符数组指针
char val[] = value; // 遍历每个字符
for (int i = 0; i < value.length; i++) {
// 31 * h会被JVM优化成(h << 5) - h
h = 31 * h + val[i];
}
// 修改字符串缓存散列码,避免重复计算
hash = h;
}
return h;
}
参考资料
《Effective Java 中文版 第2版》 第8条:覆盖equals时请遵守通用约定 P28-38
《Effective Java 中文版 第2版》 第9条:覆盖equals时总要覆盖hashCode P39-43
《Java编程思想》 P495
Java equals()方法和hashCode()方法的更多相关文章
- Java基础系列-equals方法和hashCode方法
原创文章,转载请标注出处:<Java基础系列-equals方法和hashCode方法> 概述 equals方法和hashCode方法都是有Object类定义的. publi ...
- Java 如何重写对象的 equals 方法和 hashCode 方法
前言:Java 对象如果要比较是否相等,则需要重写 equals 方法,同时重写 hashCode 方法,而且 hashCode 方法里面使用质数 31.接下来看看各种为什么. 一.需求: 对比两个对 ...
- 详解equals()方法和hashCode()方法
前言 Java的基类Object提供了一些方法,其中equals()方法用于判断两个对象是否相等,hashCode()方法用于计算对象的哈希码.equals()和hashCode()都不是final方 ...
- 关于Object类的equals方法和hashCode方法
关于Object类的equals的特点,对于非空引用: 1.自反性:x.equals(x) return true : 2.对称性:x.equals(y)为true,那么y.equals(x)也为tr ...
- HashSet中存方用户自己定义数据类型数据,重写equals方法和hashCode方法
import java.util.Set; import java.util.HashSet; public class SetTest { public static void main(Strin ...
- 详解 equals() 方法和 hashCode() 方法
创建实体类时,最好重写超类(Object)的hashCode()和equals()方法 equals()方法: 通过该实现可以看出,Object类的实现采用了区分度最高的算法,即只要两个对象不是同一个 ...
- equals()方法和hashCode()方法详解
equals()方法和hashCode()方法详解 1. Object类中equals()方法源代码如下所示: /** * Object类中的equals()方法 */ public boolean ...
- java中equals方法和hashcode方法的区别和联系,以及为什么要重写这两个方法,不重写会怎样
一.在Object类中的定义为:public native int hashCode();是一个本地方法,返回的对象的地址值.但是,同样的思路,在String等封装类中对此方法进行了重写.方法调用得到 ...
- JAVA正确地自定义比较对象---如何重写equals方法和hashCode方法
在实际应用中经常会比较两个对象是否相等,比如下面的Address类,它有两个属性:String province 和 String city. public class Address { priva ...
随机推荐
- Contest2195 - 2019-4-25 高一noip基础知识点 测试8 题解版
(因为david_alwal太懒了,所以本期题解作者为Th Au K,码风不同请自行适应) 传送门 T1 BFS?贪心?我也说不清 反正就是对每一个“#”搜一下他的旁边有没有“#”就行了 代码 T2 ...
- Fatal error encountered during command execution
MySQL + .net + EF 开发环境,调用一处sql语句报错: Fatal error encountered during command execution[sql] view plain ...
- HDU - 1062
格式错误2遍:没考虑到连续两个空格的情况,遇到空格最后要输出这个空格,因为题目只需要转换单词. 另外,开cin,cout加速要注意读入不能用scanf,printf,puts,getchar这些.ge ...
- 团队作业第六次-团队Github实战训练
格式描述 课程名称:软件工程1916|W(福州大学) 作业要求:项目系统设计与数据库设计 团队名称:为了交项目干杯 GitHub地址:地址 作业目标:搭建一个相对公平公正的抽奖系统,根据QQ聊天记录, ...
- Pompholyx - Causes, Symptoms and Treatment
Pompholyx is a common type of eczema. It is also known as dyshidrotic eczema or vesicular eczema of ...
- MySQL之CONCAT()的用法
mysql CONCAT()函数用于将多个字符串连接成一个字符串,是最重要的mysql函数之一,下面就将为您详细介绍mysql CONCAT()函数,供您参考 mysql CONCAT(str1,st ...
- -bash: yum: command not found
在学习Linux时,发现yum命令无法执行.便查了下,整理成文. yum介绍 Yum(全称为 Yellow dogUpdater, Modified)是一个在Fedora和RedHat以及CentOS ...
- 在Cyclone IVE中使用进位链的几个规则
最近在FPGA上做ps级的Delay line,所以认真剖析了一下Cyclone IVE4的布局布线延迟.这里说明CARRY链的几个特性规则,如有错误请各位大大指出,谢谢.(另外由于匆忙没有时间验证其 ...
- idea中classpath的问题
新创建的项目,在web.xml中配置 Spring MVC 的 DispatcherServlet,,指定springmvc.xml,结果这部分出错,请求的时候报错 结果查找,发现在Idea中,cla ...
- Arrays类的运用,二分法,数组的复制,命令行参数的运用,二维数组,Object,equals
/*Arrays jdk中为了便于开发,给开发者提供了Arrays类, 其中包含了很多数组的常用操作.例如快速输出.排序.查找等.*/ import java.util.Arrays; public ...