Java 重写 equals 与 hashCode 的注意事项
为什么重写 equals 的时候必须重写 hashCode
大家可能从很多教程中了解到:
SUN官方的文档中规定"如果重定义equals方法,就必须重定义hashCode方法,以便用户可以将对象插入到散列(哈希)表中"
那么 SUN 公司是出于什么考虑做了这个规定呢?
在集合框架中的HashSet,HashTable和HashMap都使用哈希表的形式存储数据,而hashCode计算出来的哈希码便是它们的身份证。哈希码的存在便可以:
- 快速定位对象,提高哈希表集合的性能。
- 只有当哈希表中对象的索引即
hashCode和对象的属性即equals同时相等时,才能够判断两个对象相等。 - 从上面可以看出,哈希码主要是为哈希表服务的,其实如果不需要使用哈希表,也可以不重写
hashCode。但是SUN公司应该是出于对程序扩展性的考虑(万一以后需要将对象放入哈希表集合中),才会规定重写equals的同时需要重写hashCode,以避免后续开发不必要的麻烦。
重写equals的注意事项
Java语言规范要求equals需要具有如下的特性:
- 自反性:对于任何非空引用 x,
x.equals()应该返回true。 - 对称性:对于任何引用 x 和 y,当且仅当
y.equals(x)返回true,x.equals(y)也应该返回true。 - 传递性:对于任何引用 x、y 和 z,如果
x.equals(y)返回true,y.equals(z)也应返回同样的结果。 - 一致性:如果 x 和 y 引用的对象没有发生变化,反复调用
x.equals(y)应该返回同样的结果。 - 对于任意非空引用 x,
x.equals(null)应该返回false。
在对象比较时,我们应该如何编写出一个符合特性的 equals 方法呢,《Core Java》中提出了如下建议:
- 显式参数命名为 otherObject,稍后将它转换成另一个叫做 other 的变量。
检测 this 与 otherObject 是否引用同一个对象:
if (this == otherObject) return true;
计算这个等式可以避免一个个比较类中的域,实现优化。
检测 otherObject 是否为 null,如果为 null,返回 false。进行非空校验是十分重要的。
比较 this 与 otherObject 是否属于同一个类。
- 如果每个子类都重写了
equals,使用getClass检验:
if (getClass() != otherObject.getClass())
return false;- 如果所有子类都使用同一个
equals,就用instanceof检验:
if (!(otherObject instanceof ClassName))
return false;- 如果每个子类都重写了
将 otherObject 转换为相应的类型变量。
ClassName other = (ClassName) otherObject;
现在可以对所有需要比较的域进行比较了。
- 基本类型使用
==比较 - 对象使用
equals比较 - 数组类型的域可以使用静态方法
Arrays.equals检测相应数组元素是否相等 - 如果所有域匹配,则返回 true
- 基本类型使用
注意:子类重写父类 equals 方法时,必须完全覆盖父类方法,不能因为类型错误或者其他原因定义了一个完全无关的方法。可以使用 @Override 注解对覆盖父类的方法进行标记,这样编译器便会检测到覆盖过程中的错误。
重写 hashCode 的注意事项
散列码(hash code)是由对象导出的一个整型值。散列码没有规律,在不同的对象中通过不同的算法生成,Java中生成 hashCode 的策略为(以下说明均摘自 Java API 8):
String 类的 hashCode 根据其字符串内容,使用算法计算后返回哈希码。
Returns a hash code for this string. The hash code for a String object is computed as s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
Integer 类返回的哈希码为其包含的整数数值。
Returns: a hash code value for this object, equal to the primitive int value represented by this Integer object.
Object 类的 hashCode 返回对象的内存地址经过处理后的数值。
Returns a hash code value for the object. This method is supported for the benefit of hash tables such as those provided by HashMap.
在自己的类中想要重写 hashCode 的话一般怎么做呢?建议合理地组合实例域的散列码,让各个不同对象产生的散列码更加均匀。例如我们现在有一个 Cat 对象,它有 name、size 和 color 三个不同域,那么可以重写 hashCode 方法如下:
class Cat {
......
public int hashCode() {
//hashCode是可以返回负值的
return 6 * name.hashCode()
+ 8 * new Double(size).hashCode()
+ 10 * color.hashCode();
}
......
}
当然还有更好的做法,我们可以直接调用静态方法 Objects.hash 并提供多个参数。这个方法会对各个参数调用 Object.hashCode,并组合返回的散列码。故以上的方法可以缩写为:
public int hashCode() {
return Objects.hash(name, size, color);
}
注意: equals与hashCode的定义必须一致,两个对象equals为true,就必须有相同的hashCode。例如:如果定义的equals比较的是小猫的 name,那么hashCode就需要散列该 name,而不是小猫的 color 或 size。
参考:
- 《Core Java》卷一
- 【哈希表数据结构】【深入理解hashcode & equals】
Java 重写 equals 与 hashCode 的注意事项的更多相关文章
- Java:重写equals()和hashCode()
Java:重写equals()和hashCode() 1.何时需要重写equals() 当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念). 2.设计equals() [1]使用instan ...
- Java重写equals()和hashCode()
1.何时需要重写equals() 当一个类有自己特有的 ”逻辑相等”概念(不同于对象身份的概念). 2.设计equals() [1]使用instanceof操作符检查 ”实参是否为正确的类型”. [2 ...
- java重写equals和hashCode方法
一.重写equals方法 如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等. 利用equals比较八大包装对象(如int,f ...
- hashCode之二--Java:重写equals()和hashCode()
以下内容总结自<Effective Java>. 1.何时需要重写equals() 当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念). 2.设计equals() [1]使用in ...
- Java 重写equals()与hashCode()方法
List对象的contains方法实际上也是调用的equals()方法来进行逐条对比的. 示例代码: package com.imooc.collection; /** * 课程类 */ public ...
- Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例(转)
Java中==.equals.hashcode的区别与重写equals以及hashcode方法实例 原文地址:http://www.cnblogs.com/luankun0214/p/4421770 ...
- java中equals与hashCode的重写问题
这几天有一个朋友问我在重写equals和hashCode上出现了问题,最后我帮她解决了问题,同时也整理出来分享给大家 现上Object的equals与HashCode的代码 public boolea ...
- Java中equals()和hashCode()的关系以及重写equals()和hashCode()的重要性
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6580647.html 一:关系 如果两个对象相等(equal),它们的hashcode一定相同: 如果两个对 ...
- Java中equals,hashcode
在Java语言中,Object对象中包含一个equals和hashCode方法,其中hashCode方法是由JVM本地代码(native code)实现的,返回值是一个有符号的32位整数,对 ...
随机推荐
- 简易高重用的jdbcutils工具封装实现类以及简易连接池实现
因为如今发现做个小项目都是导入n多的依赖包,非常烦琐,仅仅想快点开发完一个个的小需求项目,这个时候真心不想用框架,仅仅能自己写个jdbcutils,尽管网上有非常多有apache的,阿里的,可是感觉用 ...
- 浅谈struts2标签中的2个非经常常使用的标签的使用方法(radio和select)
1.如图所看到的我们须要在前台的页面通过radio和select将相应的数据库中的数据显示到选项其中,这也是我们做项目中常常须要做的,动态的显示,而不是静态的显示. 首先我们须要在页面中导入strut ...
- mysql学习笔记(编辑中)
1.LAST_INSERT_ID()依赖于当前服务器会话中自动产生的序列编号.同时括号中也可以赋值更新数据,eg: update test set id = last_insert_id(id+10) ...
- MongoDB在Win10下的安装
原文地址:http://blog.csdn.net/polo_longsan/article/details/52430539 1.下载MongoDB在windows下的安装文件 首先去官网https ...
- PHP框架认识初步
PHP框架比較 CodeIgniter Codeigniter 相当轻量级.下载下来就能用, CI 的最大特点就是可扩展性非常强 你能够通过不改动源代码的方式 优雅的扩展差点儿全部的东西. think ...
- A股市场底部顶部历史数据
1. A股市场平均市盈率 大顶沪市平均市盈率:66-70倍. A股市场2次大底沪市平均市盈率:12倍-15倍. 大底时的例子. 2005年6月6日上证指数1000点时的14倍市盈率.2008年10月2 ...
- angularjs中的$q
先说说什么是Promise,什么是$q吧.Promise是一种异步处理模式,有很多的实现方式,比如著名的Kris Kwal's Q还有JQuery的Deffered. 什么是Promise 以前了解过 ...
- Sphinx初探之安装
在Centos or redhat 安装Sphinx .首先安装依赖包 $ yum install postgresql-libs unixODBC .安装软件 $ rpm -Uhv sphinx-- ...
- excel批量取消隐藏工作表
按下"Alt+F11"键,在打开的"Microsoft Bisual Basic"窗口中,选择"插入——模块".,复制下面的代码,按F5键运 ...
- centos7 安装postgresql10
https://blog.csdn.net/rudy5348/article/details/79299162