为什么重写equals方法时,要求必须重写hashCode方法?
1 equals方法
Object类中默认的实现方式是 : return this == obj 。那就是说,只有this 和 obj引用同一个对象,才会返回true。
而我们往往需要用equals来判断 2个对象是否等价,而非验证他们的唯一性。这样我们在实现自己的类时,就要重写equals.
按照约定,equals要满足以下规则。
- 自反性: x.equals(x) 一定是true
- 对null: x.equals(null) 一定是false
- 对称性: x.equals(y) 和 y.equals(x)结果一致
- 传递性: a 和 b equals , b 和 c equals,那么 a 和 c也一定equals。
- 一致性: 在某个运行时期间,2个对象的状态的改变不会影响equals的决策结果,那么,在这个运行时期间,无论调用多少次equals,都返回相同的结果。
举例:重写equals方法
class Test
{
private int num;
private String data; public boolean equals(Object obj)
{
if (this == obj)
return true; if ((obj == null) || (obj.getClass() != this.getClass()))
return false; //能执行到这里,说明obj和this同类且非null。
Test test = (Test) obj;
return num == test.num&& (data == test.data || (data != null && data.equals(test.data)));
} public int hashCode()
{
//重写equals,也必须重写hashCode。具体后面介绍。
} }
2 hashCode方法
这个方法返回对象的散列码,返回值是int类型的散列码。
对象的散列码是为了更好的支持基于哈希机制的Java集合类,例如 Hashtable, HashMap, HashSet 等。
关于hashCode方法,一致的约定是:
- 在某个运行时期间,只要对象的(字段的)变化不会影响equals方法的决策结果,那么,在这个期间,无论调用多少次hashCode,都必须返回同一个散列码。
- 如果2个对象通过equals调用后返回是true,那么这个2个对象的has
- hCode方法也必须返回同样的int型散列码如果2个对象通过equals返回false,他们的hashCode返回的值允许相同。(然而,程序员必须意识到,hashCode返回独一无二的散列码,会让存储这个对象的hashtables更好地工作。)
重写了euqls方法的对象必须同时重写hashCode()方法。
3 为什么必须重写hashCode方法?
在上面的例子中,Test类对象有2个字段,num和data,这2个字段代表了对象的状态,他们也用在equals方法中作为评判的依据。那么, 在hashCode方法中,这2个字段也要参与hash值的运算,作为hash运算的中间参数。这点很关键,这是为了遵守:2个对象equals,那么 hashCode一定相同规则。
也是说,参与equals函数的字段,也必须都参与hashCode 的计算。
4 重写hashCode时注意事项
重写hashCode方法时除了上述一致性约定,还有以下几点需要注意:
(1)返回的hash值是int型的,防止溢出。
(2)不同的对象返回的hash值应该尽量不同。(为了hashMap等集合的效率问题)
(3)《Java编程思想》中提到一种情况
“设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该产生同样的值。如果在讲一个对象用put()添加进HashMap时产生一个hashCdoe值,而用get()取出时却产生了另一个hashCode值,那么就无法获取该对象了。所以如果你的hashCode方法依赖于对象中易变的数据,用户就要当心了,因为此数据发生变化时,hashCode()方法就会生成一个不同的散列码”。
举个例子
public class Test {
private int num;
private String data;
public Test(int num,String data){
this.num = num;
this.data = data;
}
public void setNum(int num) {
this.num = num;
}
public boolean equals(Object obj)
{
if (this == obj)
return true;
if ((obj == null) || (obj.getClass() != this.getClass()))
return false;
Test test = (Test) obj;
return num == test.num&& (data == test.data || (data != null && data.equals(test.data)));
}
public int hashCode()
{
int hash = 7;
hash = 31*hash+num;
hash = 31*hash+data.hashCode();
return hash;
}
public static void main(String[] args) {
Map<Test,Integer> map = new HashMap<>();
Test t1 = new Test(21,"ouym");
map.put(t1, 1);
t1.setNum(20);
System.out.println(map.get(t1));
}
}
输出值为null,我的天呐,hashMap取不到值了。
不重写equals和hashCode方法的话是不依赖于对象属性的变化的,也就是说这里使用默认的hashCode方法可以取到值。但是我们重写equal方法的初衷是判定name和num属性都相等的Test对象是相等的,而不是说同一个对象的引用才相等,而num=21和num=20明显不想等,所以这里hashCode返回值不同并不违背设计的初衷。注意上面代码的使用陷阱。
来自:https://www.cnblogs.com/lulipro/p/5628750.html
为什么重写equals方法时,要求必须重写hashCode方法?的更多相关文章
- 第九条:覆盖equals方法时总要覆盖hashCode方法
Object类的hashCode方法: public native int hashCode(); 是一个本地方法. 其中这个方法的主要注释如下: Whenever it is invoked o ...
- 【Java实战】源码解析为什么覆盖equals方法时总要覆盖hashCode方法
1.背景知识 本文代码基于jdk1.8分析,<Java编程思想>中有如下描述: 另外再看下Object.java对hashCode()方法的说明: /** * Returns a hash ...
- 半夜思考, 为什么建议重写 equals() 方法时, 也要重写 hashCode() 方法
我说的半夜, 并不是真正的半夜, 指的是在我一个人的时候, 我会去思考一些奇怪的问题. 要理解 hashCode() 需要理解下面三个点: hash契约 哈希冲突 哈希可变 第一点: hash 契约指 ...
- 覆写equals方法为什么需要覆写hashCode方法
覆写equals方法必须覆写hashCode方法,是JDK API上反复说明的,不过为什么要这样做呢?这两个方法之间有什么关系呢? void test() { // Person类的实例作为Map的k ...
- 在IE浏览器中执行OpenFlashChart的reload方法时无法刷新的解决方法
由于项目需求,需要在网页上利用图表展示相关数据的统计信息,采用了OpenFlashChart技术.OpenFlashChart是一款开源的以Flash和Javascript为技术基础的免费图表,用它能 ...
- 第八条——覆盖equals方法时需遵守的通用约定
1)自反性 对于任何非null的引用值x,x.equals(x)必须返回true.---这一点基本上不会有啥问题 2)对称性 对于任何非null的引用值x和y,当且仅当x.equals(y)为true ...
- 为什么重写equals时必须重写hashCode方法?
原文地址:http://www.cnblogs.com/shenliang123/archive/2012/04/16/2452206.html 首先我们先来看下String类的源码:可以发现Stri ...
- 为什么重写equals时必须重写hashCode方法?(转发+整理)
为什么重写equals时必须重写hashCode方法? 原文地址:http://www.cnblogs.com/shenliang123/archive/2012/04/16/2452206.html ...
- Effective Java 第三版——11. 重写equals方法时同时也要重写hashcode方法
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- why在重写equals时还必须重写hashcode方法
首先我们先来看下String类的源码:可以发现String是重写了Object类的equals方法的,并且也重写了hashcode方法 public boolean equals(Object anO ...
随机推荐
- 洛谷 P2041 分裂游戏 解题报告
P2041 分裂游戏 题目描述 有一个无限大的棋盘,棋盘左下角有一个大小为 n 的阶梯形区域,其中最左下角的那个格子里有一枚棋子.你每次可以把一枚棋子"分裂"成两枚棋子,分别放在原 ...
- 静态区间第k大 树套树解法
然而过不去你谷的模板 思路: 值域线段树\([l,r]\)代表一棵值域在\([l,r]\)范围内的点构成的一颗平衡树 平衡树的\(BST\)权值为点在序列中的位置 查询区间第\(k\)大值时 左区间在 ...
- DLP与上网行为管理的差别总结
参考网康和深信服的上网行为管理手册,总结了一下DLP与上网行为管理的差别: DLP与上网行为管理在功能和使用目的上有很大不同.主要体现在: 上网行为管理主要是对用户访问目标URL过滤,应用端口限制,上 ...
- bzoj 3160 万径人踪灭 FFT
万径人踪灭 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1936 Solved: 1076[Submit][Status][Discuss] De ...
- Java并发(3)- 聊聊Volatile
引言 谈到volatile关键字,大多数开发者都有一定了解,可以说是开发者非常熟悉,深入之后又非常陌生的一个关键字.相当于轻量的synchronized,也叫轻量级锁,与synchronized相比性 ...
- hdu 4359 dp
/* 题目大意:给n个节点的二叉树第i个节点的权值为2^(i-1), 求所有含左右子树的节点都符合左子树的权和小于右子树权和的种数. */ #include <iostream> #inc ...
- poj1039 Pipe(计算几何叉积求交点)
F - Pipe Time Limit:1000MS Memory Limit:10000KB 64bit IO Format:%I64d & %I64u Submit Sta ...
- for...in与点语法
语法 for...in语句循环一个指定的变量来循环一个对象所有可枚举的属性.如下所示 for (variable in object){ statements } 问题 在实际的使用过程中发现,在fo ...
- sqlalchemy多表查询
from datetime import datetime from sqlalchemy import Column,Integer,String,Boolean,DateTime,ForeignK ...
- 【原创】Linux环境下的图形系统和AMD R600显卡编程(8)——AMD显卡DRM驱动初始化过程
前面几个blog对DRM驱动.显卡的显存管理机制.中断机制都进行了一些描述,现在阅读AMD drm驱动的初始化过程应该会轻松许多. 下面是一AMD的开发人员编写的文章(先暂时放在这里,后续有时间再添加 ...