内存泄漏避雷!你真的了解重写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对象相同,不需要判断它们所有字段都相同,只需要判断它们的某 ...
随机推荐
- Nginx支持WebSocket反向代理
WebSocket是目前比较成熟的技术了,WebSocket协议为创建客户端和服务器端需要实时双向通讯的webapp提供了一个选择.其为HTML5的一部分,WebSocket相较于原来开发这类app的 ...
- vue2与vue3的差异(总结)?
vue作者尤雨溪在开发 vue3.0 的时候开发的一个基于浏览器原生 ES imports 的开发服务器(开发构建工具).那么我们先来了解一下vite Vite Vite,一个基于浏览器原生 ES i ...
- vs2012换肤功能,vs2012主题及自定义主题
vs2012 默认的主题只有浅色和深色两种,深色太黑了,浅色又太亮了!怎么办? 研究不少时间终于找到了怎么编辑vs2012的皮肤 现在开始吧 打开vs2012-工具--扩展和更新 在联机中搜索 The ...
- [bzoj4942]整数
考虑暴力,即需要考虑如何实现$\pm 2^{k}$,相当于要找到之后的第一个0或者之前的第一个1(维护区间是否全0/1即可),然后区间重置,可以用线段树维护,复杂度为$o(900n)$(a的划分和线段 ...
- 如何找到mysql磁盘地址
show global variables like "%datadir%"; 我的位置: C:\ProgramData\MySQL\MySQL Server 5.5\Data\
- System类的常用方法(currentTimeMillis与arraycopy)
System类的常用方法 currentTimeMillis与arraycopy import java.util.Arrays; /* java.lang.System类中提供了大量的静态方法,可以 ...
- CentOS7部署ceph
CEPH 简介 不管你是想为云平台提供Ceph 对象存储和/或 Ceph 块设备,还是想部署一个 Ceph 文件系统或者把 Ceph 作为他用,所有 Ceph 存储集群的部署都始于部署一个个 Ceph ...
- GO 语言使用copy 拷贝切片的问题
使用copy,直接改变原片的值,而不是先创建一个副本.
- mac 下 如何在同一窗口打开多个终端并实现快捷键切换
相信大家编代码的时候都会遇到,每次需要在头文件,库文件和源码文件中编代码的时候,总是需要在几个文件中切换来切换去的,而且一个文件就一个终端窗口,每次都要用鼠标点来点去,非常麻烦,所以如果能把这几个文件 ...
- goto 的用法
#include <stdio.h> int main() { printf("go to cpy \n"); goto FLASH_CPY; printf(" ...