内存泄漏避雷!你真的了解重写equals()和hashcode()方法的原因吗?

基本概念
- 要比较两个对象是否相等时需要调用对象的equals() 方法:
- 判断对象引用所指向的对象地址是否相等
- 对象地址相等时, 那么对象相关的数据也相等,包括:
- 对象句柄
- 对象头
- 对象实例数据
- 对象类型数据
- 可以通过比较对象的地址来判断对象是否相等
Object源码
- 对象在不重写的情况下使用的是Object中的equals() 方法和hashCode() 方法
- equals(): 判断的是两个对象的引用是否指向同一个对象
- hashCode(): 根据对象地址生成一个整数数值
- Object的hashCode() 方法修饰符为native: 表明该方法是由操作系统实现. Java调用操作系统底层代码获取Hash值
public native int hashCode();
重写equals
- 重写equals()方法的场景:
- 假设现在有很多学生对象
- 默认情况下,要判断多个学生对象是否相等,需要根据地址判断:
- 若对象地址相等,那么对象实例的数据一定是一样的
- 判断相等的要求:
- 当学生的姓名,年龄,性别相等时,认为对象是相等的,
- 不一定需要对象的地址完全相同
- 根据需求重写equals()方法:
public class Student {
/** 姓名 */
private String name;
/** 性别 */
private String sex;
/** 年龄 */
private String age;
/** 体重 */
private float weight;
/** 地址 */
private String addr;
/*
* 重写equals()方法
*/
@Override
public boolean equals(Object obj) {
// instanceof已经处理了obj == null的情况
if (! (Object instanceof Student)) {
return false;
}
Student stuObj = (Student) obj;
// 地址相等
if (this == stuObj) {
return true;
}
// 如果对象的姓名,年龄,性别相等.则两个对象相等
if (stuObj.name.equals(this.name) && stuObj.sex.equals(this.sex) && stuObj.age.equals(this.age)) {
return true;
} else {
return false;
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getWeight() {
return weight;
}
public void setName(String weight) {
this.weight = weight;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
}
- 示例:
public static void main(String[] args) {
Student s1 = new Student();
s1.setAddr("earth");
s1.setAge("20");
s1.setName("Tom");
s1.setSex("Male");
s1.setWeight(60f);
Student s2 = new Student();
s2.setAddr("Mars");
s2.setAge("20");
s2.setName("Tom");
s2.setSex("Male");
s2.setWeight(70f);
if (s1.equals(s2)) {
System.out.println("s1 == s2");
} else {
System.out.println("s1 != s2");
}
}
- 重写了equals() 方法后,这里会输出 [s1==s2]
- 如果没有重写 equals() 方法,那么必定会输出 [s1!=s2]
重写hashCode
- 根据重写equals的方法,上述s1和s2认为是相等的
- Object中的hashCode()方法:
- 在equals() 方法没被修改的前提下,多次调用同一个对象的hashCode() 方法返回的值必须是相同的正数
- 如果两个对象互相equals(), 那么这两个对象的hashcode值必须相等
- 为不同的对象生成不同的hashcode可以提升Hash表的性能
- 重写hashCode()方法:
```java
public class Student {
/** 姓名 */
private String name;
/** 性别 */
private String sex;
/** 年龄 */
private String age;
/** 体重 */
private float weight;
/** 地址 */
private String addr;
/*
* 重写hashCode()方法
*/
@Override
public int hashCode() {
int result = name.hashCode();
result = 17 * result + sex.hashCode();
result = 17 * result + age.hashCode();
return result;
}
/*
* 重写equals()方法
*/
@Override
public boolean equals(Object obj) {
// instanceof已经处理了obj == null的情况
if (! (Object instanceof Student)) {
return false;
}
Student stuObj = (Student) obj;
// 地址相等
if (this == stuObj) {
return true;
}
// 如果对象的姓名,年龄,性别相等.则两个对象相等
if (stuObj.name.equals(this.name) && stuObj.sex.equals(this.sex) && stuObj.age.equals(this.age)) {
return true;
} else {
return false;
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getWeight() {
return weight;
}
public void setName(String weight) {
this.weight = weight;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
}
- 在两个对象相等的情况下,分别放入Map和Set中:
public static void main(String[] args) {
Student s1 = new Student();
s1.setAddr("earth");
s1.setAge("20");
s1.setName("Tom");
s1.setSex("Male");
s1.setWeight(60f);
Student s2 = new Student();
s2.setAddr("Mars");
s2.setAge("20");
s2.setName("Tom");
s2.setSex("Male");
s2.setWeight(70f);
if (s1.equals(s2)) {
System.out.println("s1 == s2");
} else {
System.out.println("s1 != s2");
}
Set set = new HashSet();
set.add(s1);
set.add(s2);
System.out.println(Set);
}
- 如果没有重写Object的hashCode() 方法,会出现:
[com.oxford.Student@7852e922, com.oxford.Student@4e25154f]
- 这是不符合预期的,因为Set容器有去重的特性.相等的元素不会重复显示.这就涉及到Set的底层实现了
- HashSet底层实现:
- HashSet底层是通过HashMap实现的
- 比较Set容器内元素是否相等是通过比较对象的hashcode来判断是否相等的
- hashCode()的写法:
- 首先整理出判断对象相等的属性
- 然后去一个尽可能小的正整数,防止最终结果超出整型int的取数范围
- 然后计算[正整数 * 属性的hashCode + 其余某个属性的hashCode]
- 重复步骤
/*
* 重写hashCode()方法
*/
@Override
public int hashCode() {
int result = name.hashCode();
result = 17 * result + sex.hashCode();
result = 17 * result + age.hashCode();
return result;
}
原理分析
- 因为没有重写父类的Object的hashCode() 方法,所以Object的hashCode() 方法会根据两个对象的地址生成响应的hashcode
- 由于两个对象分别是实体类创建的不同的实例,所以地址肯定是不一样的,那么hashcode值也是不一样的
- Set区别对象是不是唯一的标准:
- 两个对象的hashcode值是否一样
- 然后再判定两个对象是否equals
- Map区别对象是不是唯一的标准:
- 先根据Key值的hashcode分配来获取保存数组下标
- 然后再根据eaquals区分是否是唯一值
HashMap
HashMap组成结构
- HashMap: 是由数组和链表组成的
HashMap的存储
- HashMap的存储:
- 一个对象存储到HashMap中的位置是由key的hashcode值决定的
- HashMap查找key:
- 查找key时 ,hashMap会先根据key值的hashcode经过取余算法定位所在数组的位置
- 然后根据key的equals方法匹配相同的key值获取相应的对象
- 存值规则:
- 将Key的hashcode与HashMap的容量,进行取余运算得出该Key存储在数组所在位置的下标
- HashMap查找key:
- 得到key在数组中的位置
- 匹配得到对应key值对象
- 然后将上述多个对象根据key.equals() 来匹配获取对应的key的数据对象
- HashMap中的hashCode:
- 如果没有hashcode就意味着HashMap存储的时候是没有规律可循的
- 这样每次使用map.get() 方法,就要将map里的对象一一进行equals匹配,导致效率低下
内存泄漏避雷!你真的了解重写equals()和hashcode()方法的原因吗?的更多相关文章
- Object重写equals()、hashcode()方法的原因
一.问题 在我们新建java对象的时候,如果后期用到对象比较,就必须重写equals(0.hashcode()方法 为什么必须重写这两个方法? 只是比较相等的话,重写equals()方法不就可以吗?为 ...
- Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例(转)
Java中==.equals.hashcode的区别与重写equals以及hashcode方法实例 原文地址:http://www.cnblogs.com/luankun0214/p/4421770 ...
- 为什么要重写equals和hashcode方法
equals hashcode 当新建一个java类时,需要重写equals和hashcode方法,大家都知道!但是,为什么要重写呢? 需要保证对象调用equals方法为true时,hashcode ...
- 如何正确的重写equals() 和 hashCode()方法
比较两个Java对象时, 我们需要覆盖equals和 hashCode. public class User{ private String name; private int age; priva ...
- 【转】Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例
原文地址:http://www.cnblogs.com/luankun0214/p/4421770.html 感谢网友的分享,记录下来只为学习. 1.重写equals方法实例 部分代码参考http ...
- Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例
1.重写equals方法实例 部分代码参考http://blog.csdn.net/wangloveall/article/details/7899948 重写equals方法的目的是判断两个对象 ...
- java重写equals和hashCode方法
一.重写equals方法 如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等. 利用equals比较八大包装对象(如int,f ...
- 重写equals()与hashCode()方法
出自:http://blog.csdn.net/renfufei/article/details/16339351 Java语言是完全面向对象的,在java中,所有的对象都是继承于Object类.Oj ...
- 重写equals() 和 hashCode()方法
什么情况下需要重写呢? 比如去重操作时, 有时候往Set集合存放对象User,我们User类的字段太多时,比如有50个字段, 判断两个User对象相同,不需要判断它们所有字段都相同,只需要判断它们的某 ...
随机推荐
- 菜鸡的Java笔记 第七 - java 数组
数组的基本概念 笔试中经常出现的试题或是问题 1.1 概念 数组指的是一组相关变量的集合 如果用最原始的方式定义100个变量的话没问题但是这些变量的关联实在 ...
- 加班时的灵感迸发,我用selenium做了个窗口化的爬*宝数据。(附源码链接)
完整代码&火狐浏览器驱动下载链接:https://pan.baidu.com/s/1pc8HnHNY8BvZLvNOdHwHBw 提取码:4c08 双十一刚过,想着某宝的信息看起来有些少很难做 ...
- [cf1261F]Xor-Set
构造一棵权值范围恰为$[0,2^{60})$的权值线段树,考虑其中从下往上第$h$层($0\le h\le 60$)中的一个区间,假设其左端点为$l$,即$[l,l+2^{h})$ 这样的一个区间具有 ...
- Ubuntu下的磁盘管理
采用fat的磁盘存储,插入后采用相同命令会出现sdb和sdb1 sdb:磁盘 sdb1:磁盘分区标号为1 命令 df:显示磁盘使用情况 du:查询某个文件的大小读 du-h 或du -h --max- ...
- 详解Python Streamlit框架,用于构建精美数据可视化web app,练习做个垃圾分类app
今天详解一个 Python 库 Streamlit,它可以为机器学习和数据分析构建 web app.它的优势是入门容易.纯 Python 编码.开发效率高.UI精美. 上图是用 Streamlit 构 ...
- 反调试代码调试死机代码禁止F12代码
反调试代码调试死机代码禁止F12代码 // 反调试函数,参数:开关,执行代码 function siji(){ var total=""; for (var i=0; ...
- c# System.Text.Json 精讲
本文内容来自我写的开源电子书<WoW C#>,现在正在编写中,可以去WOW-Csharp/学习路径总结.md at master · sogeisetsu/WOW-Csharp (gith ...
- 【Azure 环境】在Windows环境中抓取网络包(netsh trace)后,如何转换为Wireshark格式以便进行分析
问题描述 如何在Windows环境中,不安装第三方软件的情况下(使用Windows内置指令),如何抓取网络包呢?并且如何转换为Wireshark 格式呢? 操作步骤 1) 以管理员模式打开CMD,使用 ...
- 【树莓派】Python开发工控机急停设计
背景 我们在一些工业产品中使用树莓派替代了PLC和上位机,并借助树莓派的算力将AI和机器视觉引入工业领域. 以前的产品都不存在动作机构,仅仅将结果输出到指示灯.蜂鸣器或者显示器上,没有安全隐患, 现在 ...
- CF1264D2 Beautiful Bracket Sequence (hard version)
考虑\(D1\)的\(O(n^2)\),我们直接进行组合处理. 考虑在\(p\)这个位置,左边有\(l\)个(,右边有\(r\)个),左边有\(l\)个问号,右边有\(r\)个问号. 这个位置的贡献为 ...