基本概念

  • 要比较两个对象是否相等时需要调用对象的equals() 方法:

    • 判断对象引用所指向的对象地址是否相等
  • 对象地址相等时, 那么对象相关的数据也相等,包括:
    • 对象句柄
    • 对象头
    • 对象实例数据
    • 对象类型数据
  • 可以通过比较对象的地址来判断对象是否相等

Object源码

  • 对象在不重写的情况下使用的是Object中的equals() 方法和hashCode() 方法

    • equals(): 判断的是两个对象的引用是否指向同一个对象
    • hashCode(): 根据对象地址生成一个整数数值
  • ObjecthashCode() 方法修饰符为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);
}
  • 如果没有重写ObjecthashCode() 方法,会出现:
[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;
}

原理分析

  • 因为没有重写父类的ObjecthashCode() 方法,所以ObjecthashCode() 方法会根据两个对象的地址生成响应的hashcode
  • 由于两个对象分别是实体类创建的不同的实例,所以地址肯定是不一样的,那么hashcode值也是不一样的
  • Set区别对象是不是唯一的标准:
    • 两个对象的hashcode值是否一样
    • 然后再判定两个对象是否equals
  • Map区别对象是不是唯一的标准:
    • 先根据Key值的hashcode分配来获取保存数组下标
    • 然后再根据eaquals区分是否是唯一值

HashMap

HashMap组成结构

  • HashMap: 是由数组链表组成的

HashMap的存储

  • HashMap的存储:

    • 一个对象存储到HashMap中的位置是由keyhashcode值决定的
    • HashMap查找key:
      • 查找key,hashMap会先根据key值的hashcode经过取余算法定位所在数组的位置
      • 然后根据keyequals方法匹配相同的key值获取相应的对象
  • 存值规则:
    • KeyhashcodeHashMap的容量,进行取余运算得出该Key存储在数组所在位置的下标
  • HashMap查找key:
    • 得到key在数组中的位置
    • 匹配得到对应key值对象
    • 然后将上述多个对象根据key.equals() 来匹配获取对应的key的数据对象
  • HashMap中的hashCode:
    • 如果没有hashcode就意味着HashMap存储的时候是没有规律可循的
    • 这样每次使用map.get() 方法,就要将map里的对象一一进行equals匹配,导致效率低下

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

  1. Object重写equals()、hashcode()方法的原因

    一.问题 在我们新建java对象的时候,如果后期用到对象比较,就必须重写equals(0.hashcode()方法 为什么必须重写这两个方法? 只是比较相等的话,重写equals()方法不就可以吗?为 ...

  2. Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例(转)

    Java中==.equals.hashcode的区别与重写equals以及hashcode方法实例  原文地址:http://www.cnblogs.com/luankun0214/p/4421770 ...

  3. 为什么要重写equals和hashcode方法

    equals hashcode  当新建一个java类时,需要重写equals和hashcode方法,大家都知道!但是,为什么要重写呢? 需要保证对象调用equals方法为true时,hashcode ...

  4. 如何正确的重写equals() 和 hashCode()方法

    比较两个Java对象时, 我们需要覆盖equals和  hashCode. public class User{ private String name; private int age; priva ...

  5. 【转】Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例

    原文地址:http://www.cnblogs.com/luankun0214/p/4421770.html 感谢网友的分享,记录下来只为学习. 1.重写equals方法实例   部分代码参考http ...

  6. Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例

    1.重写equals方法实例   部分代码参考http://blog.csdn.net/wangloveall/article/details/7899948 重写equals方法的目的是判断两个对象 ...

  7. java重写equals和hashCode方法

    一.重写equals方法 如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等. 利用equals比较八大包装对象(如int,f ...

  8. 重写equals()与hashCode()方法

    出自:http://blog.csdn.net/renfufei/article/details/16339351 Java语言是完全面向对象的,在java中,所有的对象都是继承于Object类.Oj ...

  9. 重写equals() 和 hashCode()方法

    什么情况下需要重写呢? 比如去重操作时, 有时候往Set集合存放对象User,我们User类的字段太多时,比如有50个字段, 判断两个User对象相同,不需要判断它们所有字段都相同,只需要判断它们的某 ...

随机推荐

  1. [loj150]挑战多项式

    以NTT为运算基础,即默认支持在$o(n\log n)$的时间内多项式乘法 二次剩余:称$n$为模$p$意义下的二次剩余,当且仅当存在$x$使得$x^{2}\equiv n(mod\ p)$ 当$p$ ...

  2. Spring Cloud Gateway限流实战

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  3. DPC++中的现代C++语言特性

    Ⅰ DPC++简介 DPC++是Data Parallel C++(数据并行C++)的首字母缩写,它是Intel为了将SYCL引入LLVM和oneAPI所开发的开源项目.SYCL是为了提高各种加速设备 ...

  4. Java设计模式之(十二)——观察者模式

    1.什么是观察者模式? Define a one-to-many dependency between objects so that when one object changes state, a ...

  5. C#.NET 操作Windows服务承载WCF

    Windows服务的制作.安装可以参考这篇: C#.NET 操作Windows服务(安装.卸载) - runliuv - 博客园 (cnblogs.com) 本篇会在这个解决方案基础上,继续修改. 一 ...

  6. [APIO2020]有趣的旅途

    注意到第一个点是可以钦定的. 那么我们考虑在重心的子树里反复横跳. 每次选择不同子树里的深度最大的点. 在同一颗子树里可能会在lca处出现问题. 那么我们选择重心,要考虑到会不会出现一颗子树不够选的操 ...

  7. NFLSOJ 1060 - 【2021 六校联合训练 NOI #40】白玉楼今天的饭(子集 ln)

    由于 NFLSOJ 题面上啥也没有就把题意贴这儿了( 没事儿,反正是上赛季的题,你们非六校学生看了就看了,况且看了你们也没地方交就是了 题意: 给你一张 \(n\) 个点 \(m\) 条边的图 \(G ...

  8. 【5】蛋白质组学鉴定定量软件之PD

    目录 1.简介 2.安装与配置 3.分析流程 4.结果 1.简介 PD全称Proteome Discoverer,是ThermoFisher在2008年推出的商业Windows软件,没错,收费,还不菲 ...

  9. Augustus指南(Trainning部分)

    Augustus指南 官方 Tutorial Index Augustus是一个真核生物基因预测软件,目前有网页服务端和本地版,它基于Hidden-Markov Model(隐马尔科夫链模型HMM)( ...

  10. lilo.conf

    描述 默认情况下,本文件 ( /etc/lilo.conf ) 由引导管理程序 lilo 读取 (参考 lilo(8)). 它看起来可能象这样: boot = /dev/hda delay = 40 ...