内存泄漏避雷!你真的了解重写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对象相同,不需要判断它们所有字段都相同,只需要判断它们的某 ...
随机推荐
- win10开机自启
所有用户的开机自启文件夹 英文路径 # ProgramData目录是隐藏的 C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp 中 ...
- [loj502]ZQC的截图
给每一个人一个随机数$R_{i}$,将一个消息中所有人的的$R_{i}$在三进制下相加(多次出现需要多个$R_{i}$),最终之和若为0,即判定答案为-1,若为某个$R_{i}$或$R_{i}+R_{ ...
- [bzoj1593]旅馆
用线段树维护区间中最大的一段连续的1,以左端点为左端点最大的一段连续的1,以右端点为右端点最大的一段连续的1,然后就可以支持区间修改和查询了 1 #include<bits/stdc++.h&g ...
- 【Azure 应用服务】App Service 无法连接到Azure MySQL服务,报错:com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
问题描述 App Service使用jdbc连接MySQL服务,出现大量的 Communications link failure: com.mysql.cj.jdbc.exceptions.Com ...
- SpringCloud微服务实战——搭建企业级开发框架(二十五):实现多租户多平台短信通知服务
目前系统集成短信似乎是必不可少的部分,由于各种云平台都提供了不同的短信通道,这里我们增加多租户多通道的短信验证码,并增加配置项,使系统可以支持多家云平台提供的短信服务.这里以阿里云和腾讯云为例,集成短 ...
- 『学了就忘』Linux权限管理 — 56、不可改变位权限(chattr)
目录 1.命令格式 2.查看文件系统属性chattr权限 3.示例 文件系统属性chattr权限,也叫不可改变位权限,该权限没有风险,但是他能限制root用户. 1.命令格式 [root@localh ...
- 回文字符串 Manacher
1. Manacher 忘光了,忘光了. 首先将字符串所有字符之间(包括头尾)插入相同分隔符,再在最前方插入另一个分隔符防止越界. 设以 \(s_i\) 为对称中心的回文串中,最长的回文半径为 \(p ...
- 【宏蛋白组】iMetaLab平台分析肠道宏蛋白质组数据
目录 一.iMetaLab简介 二.内置工具与模块 1. Data Processing module 2. Functional Analysis 3. R Developing environme ...
- kubernetes部署 kube-apiserver服务
kubernetes部署 kube-apiserver 组件 本文档讲解使用 keepalived 和 haproxy 部署一个 3 节点高可用 master 集群的步骤. kube-apiserve ...
- FFmpeg笔记:使用MSVC工具链编译Windows版本静态库、动态库
2019年3月开始,为了将音视频编解码功能集成到Cocos2d-x中,开始接触到FFmpeg: 当时开发环境还在Mac下,编译FFmpeg相比现在用Windows平台要方便的多: 最近,公司内部有个U ...