【Java】杂项、Equals 和 hashCode

Google Guava实战指南:从基础API到短链接服务完整示例 Java开发中常见的判空、拼接、集合转换等重复代码,不仅耗时而且易错,这时引入Google Guava这类Java工具库可以显著提升编码效率。作为Google开源的核心库,Guava在集合、字符串处理、本地缓存、事件总线、哈希等领域提供了高效API。从不可变集合到Multimap、BiMap等新集合类型,从Joiner与Splitter到Preconditions参数校验,再到Guava Cache解决手写缓存的线程安全和过期策略痛点,以及EventBus实现模块间解耦,配合Hashing生成哈希指纹,都是 阅读详情

equals 和 hashCode含义

equal:判断两个对象是否相等,如果相同,返回true 否则返回false

hashcode: 返回一个int数 Object 默认(内部地址转化为一个数字)

 

两者关系

  • 如果equals(obj)返回true, 那么 hashCode一定返回一样的值

  • 如果equals(obj)返回false, 那么 hashCode 不一定返回一样的值

  • 如果hashCode 返回相同的数,那么 equals 不一定返回true

  • 如果hashCode返回不一样的数,那么equals 一定返回false

 

集合中使用

放入流程

放入散列时候,先比较hashCode, 然后判断equals

HashSet 放入元素

public boolean add(E e) {  
    return map.put(e, PRESENT)==null;  
}

 

HashMap 放入元素

public V put(K key, V value) {  
    if (key == null)  
        return putForNullKey(value);  
    int hash = hash(key.hashCode());  
    int i = indexFor(hash, table.length);  
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
        Object k;  
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
            V oldValue = e.value;  
            e.value = value;  
            e.recordAccess(this);  
            return oldValue;  
        }  
    }  

    modCount++;  
    addEntry(hash, key, value, i);  
    return null;  
}

测试一: 覆盖 equals 不覆盖 hashCode

public class HashCodeTest {  
    public static void main(String[] args) {  
        Collection set = new HashSet();  
        Point p1 = new Point(1, 1);  
        Point p2 = new Point(1, 1);  
  
        System.out.println(p1.equals(p2));  
        set.add(p1);   //(1)  
        set.add(p2);   //(2)  
        set.add(p1);   //(3)  
  
        Iterator iterator = set.iterator();  
        while (iterator.hasNext()) {  
            Object object = iterator.next();  
            System.out.println(object);  
        }  
    }  
}  
  
class Point {  
    private int x;  
    private int y;  
  
    public Point(int x, int y) {  
        super();  
        this.x = x;  
        this.y = y;  
    }  
  
    @Override  
    public boolean equals(Object obj) {  
        if (this == obj)  
            return true;  
        if (obj == null)  
            return false;  
        if (getClass() != obj.getClass())  
            return false;  
        Point other = (Point) obj;  
        if (x != other.x)  
            return false;  
        if (y != other.y)  
            return false;  
        return true;  
    }  
  
    @Override  
    public String toString() {  
        return "x:" + x + ",y:" + y;  
    }  
  
} 

 

输出

true  
x:1,y:1  
x:1,y:1

 

分析

(1)当执行set.add(p1)时(1),集合为空,直接存入集合;

(2)当执行set.add(p2)时(2),首先判断该对象(p2)的hashCode值所在的存储区域是否有相同的hashCode,因为没有覆盖hashCode方法,所以jdk使用默认Object的hashCode方法,返回内存地址转换后的整数,因为不同对象的地址值不同,所以这里不存在与p2相同hashCode值的对象,因此jdk默认不同hashCode值,equals一定返回false,所以直接存入集合。

(3)当执行set.add(p1)时(3),时,因为p1已经存入集合,同一对象返回的hashCode值是一样的,继续判断equals是否返回true,因为是同一对象所以返回true。此时jdk认为该对象已经存在于集合中,所以舍弃。

 

测试二:覆盖hashCode方法,但不覆盖equals方法,仍然会导致数据的不唯一性

class Point {  
    private int x;  
    private int y;  
  
    public Point(int x, int y) {  
        super();  
        this.x = x;  
        this.y = y;  
    }  
  
    @Override  
    public int hashCode() {  
        final int prime = 31;  
        int result = 1;  
        result = prime * result + x;  
        result = prime * result + y;  
        return result;  
    }  
  
    @Override  
    public String toString() {  
        return "x:" + x + ",y:" + y;  
    }  
  
}  

输出

false  
x:1,y:1  
x:1,y:1

分析

(1)当执行set.add(p1)时(1),集合为空,直接存入集合;

(2)当执行set.add(p2)时(2),首先判断该对象(p2)的hashCode值所在的存储区域是否有相同的hashCode,这里覆盖了hashCode方法,p1和p2的hashCode相等,所以继续判断equals是否相等,因为这里没有覆盖equals,默认使用'=='来判断,所以这里equals返回false,jdk认为是不同的对象,所以将p2存入集合。

(3)当执行set.add(p1)时(3),时,因为p1已经存入集合,同一对象返回的hashCode值是一样的,并且equals返回true。此时jdk认为该对象已经存在于集合中,所以舍弃。

 

综合上述两个测试,要想保证元素的唯一性,必须同时覆盖hashCode和equals才行。

(注意:在HashSet中插入同一个元素(hashCode和equals均相等)时,会被舍弃,而在HashMap中插入同一个Key(Value 不同)时,原来的元素会被覆盖。)

测试三 内存泄漏问题

public class HashCodeTest {  
    public static void main(String[] args) {  
        Collection set = new HashSet();  
        Point p1 = new Point(1, 1);  
        Point p2 = new Point(1, 2);  
  
        set.add(p1);  
        set.add(p2);  
          
        p2.setX(10);  
        p2.setY(10);  
          
        set.remove(p2);  
  
        Iterator iterator = set.iterator();  
        while (iterator.hasNext()) {  
            Object object = iterator.next();  
            System.out.println(object);  
        }  
    }  
}  
  
class Point {  
    private int x;  
    private int y;  
  
    public Point(int x, int y) {  
        super();  
        this.x = x;  
        this.y = y;  
    }  
  
  
    public int getX() {  
        return x;  
    }  
  
  
    public void setX(int x) {  
        this.x = x;  
    }  
  
  
    public int getY() {  
        return y;  
    }  
  
  
    public void setY(int y) {  
        this.y = y;  
    }  
  
  
    @Override  
    public int hashCode() {  
        final int prime = 31;  
        int result = 1;  
        result = prime * result + x;  
        result = prime * result + y;  
        return result;  
    }  
  
  
    @Override  
    public boolean equals(Object obj) {  
        if (this == obj)  
            return true;  
        if (obj == null)  
            return false;  
        if (getClass() != obj.getClass())  
            return false;  
        Point other = (Point) obj;  
        if (x != other.x)  
            return false;  
        if (y != other.y)  
            return false;  
        return true;  
    }  
  
  
    @Override  
    public String toString() {  
        return "x:" + x + ",y:" + y;  
    }  
  
}  

结果

x:1,y:1  
x:10,y:10

 

分析

假设p1的hashCode为1,p2的hashCode为2,在存储时p1被分配在1号桶中,p2被分配在2号筒中。这时修改了p2中与计算hashCode有关的信息(x和y),当调用remove(Object obj)时,首先会查找该hashCode值得对象是否在集合中。假设修改后的hashCode值为10(仍存在2号桶中),这时查找结果空,jdk认为该对象不在集合中,所以不会进行删除操作。然而用户以为该对象已经被删除,导致该对象长时间不能被释放,造成内存泄露。解决该问题的办法是不要在执行期间修改与hashCode值有关的对象信息,如果非要修改,则必须先从集合中删除,更新信息后再加入集合中。

总结

1.hashCode是为了提高在散列结构存储中查找的效率,在线性表中没有作用。

2.equals和hashCode需要同时覆盖。

3.若两个对象equals返回true,则hashCode有必要也返回相同的int数。

4.若两个对象equals返回false,则hashCode不一定返回不同的int数,但为不相等的对象生成不同hashCode值可以提高哈希表的性能。

5.若两个对象hashCode返回相同int数,则equals不一定返回true。

6.若两个对象hashCode返回不同int数,则equals一定返回false。

7.同一对象在执行期间若已经存储在集合中,则不能修改影响hashCode值的相关信息,否则会导致内存泄露问题。

 

原文:https://blog.youkuaiyun.com/haobaworenle/article/details/53819838

 

从IPv6到Tomcat:彻底解决127.0.0.1拒绝连接的完整指南 本文提供了从IPv6到Tomcat的全面指南,帮助开发者彻底解决127.0.0.1拒绝连接的问题。涵盖网络协议栈、服务配置、端口管理等多个层面的排查方法,特别针对Tomcat服务提供了专项解决方案,确保开发者能够快速定位并修复连接问题。 阅读详情

相关推荐

保姆级教程:用PCAN-View监控汽车CAN总线,从硬件连接到报文分析(附快捷键大全)

本文提供了一份详细的PCAN-View使用教程,涵盖从硬件连接到报文分析的全流程,特别适合汽车电子工程师和初学者。内容包括硬件准备、软件安装、核心功能操作、高级技巧及故障排查,并附有实用的快捷键大全,帮助用户高效监控和分析CAN总线数据。

weixin_42542669的博客 497

CTF Web实战进阶:三层渗透法与黑客思维构建

Web安全渗透测试是网络安全领域的核心技能,其本质在于理解系统架构并发现其脆弱性。从原理层面看,渗透测试遵循从信息搜集到漏洞利用的完整生命周期,涉及对网络协议、应用逻辑和系统组件的深入分析。在技术价值上,掌握系统化的测试方法能显著提升漏洞挖掘效率与防御能力,尤其适用于CTF竞赛、红蓝对抗和实际安全评估等场景。本文聚焦于CTF Web实战,通过整合漏洞探测与链式利用等关键技术,系统阐述如何构建从信息搜集、漏洞验证到边界突破的三层渗透体系,并强调工具链整合与自动化脚本在实战中的应用,帮助安全从业者及CTF选手建

shirlly 417

GMSL2摄像头接入避坑指南:从FMC子卡配置到V4L2调用的完整流程(附Ubuntu20.04实测)

本文详细解析了GMSL2摄像头接入的全流程,从FMC子卡配置到V4L2调用的完整指南。基于XC7Z100平台实测经验,提供了硬件选型、带宽优化、Linux驱动配置及高分辨率采集的实战技巧,帮助开发者规避常见问题,提升系统稳定性与性能。

weixin_33737774的博客 418

【Java杂项】equals 与 hashCode 为什么必须一起重写?从 HashMap 原理到可变 key 避坑

本文从 Java 对象相等契约出发,讲清 equals() 与 hashCode() 为什么必须一起重写:HashMap/HashSet 会先用 hashCode 定位桶,再用 equals 判断逻辑相等。文章结合 BadStudent、MutableStudent、String key、继承对称性和可变 key 等示例,梳理哈希冲突、逻辑内存泄漏和生产级 key 设计边界。

超级苦力怕的博客 1449

完整IC设计流程解析&Makefile学习

本文介绍了decorder38项目的文件结构、自动化综合流程和Makefile使用方法。项目包含RTL到GDSII全流程文件,重点关注syn目录中的综合脚本。通过make dc命令运行综合流程,生成网表、约束和时序文件。关键报告如qor.rpt、area.rpt和min_vio.rpt用于分析综合质量、面积和时序。Makefile部分解释了其基本语法、IC设计中的典型结构,包括目标定义、命令执行和特殊声明,强调了其在自动化构建和依赖管理中的重要性。

m0_63352623的博客 855

⭐️白嫖的阿里云认证⭐️ 【真题&参考答案】:大模型Clouder认证:利用大模型提升内容生产能力

【摘要】真题「大模型Clouder认证:利用大模型提升内容生产能力」

davidkorhenn的博客 1922

【Java杂项】HashMap 为什么不能用可变对象当 key?从 get 返回 null 到集合污染

本文围绕 Java `HashMap` 中“可变对象能不能当 key”展开,用最小代码解释为什么修改 key 字段后 `get()` 可能返回 `null`。文章进一步分析桶定位、`hashCode()`、`equals()`、同引用比较、跨对象相等性污染、逻辑重复 key 和 `remove()` 失败等工程问题,并给出 `entrySet()` 遍历清理、重建 Map、`final class`、`record`、Lombok `@Value` 等防御性设计建议。

超级苦力怕的博客 3944

别再对着数据手册画图了!用HFSS 2023 R2快速复现ESP-01的PCB天线(附模型文件)

本文详细介绍了使用HFSS 2023 R2逆向复现ESP-01 PCB天线的五个关键步骤,包括逆向工程准备、高效建模技巧、材料与边界设置、激励与求解配置以及结果分析与设计验证。通过参数化建模和智能边界检测,大幅提升仿真效率,适用于WIFI和蓝牙设备的射频设计优化。

weixin_30832143的博客 76

Java杂项记录

Java知识点汇总以及常见面试题 “==”和equals()的区别 ==比较基本数据类型,比较的是值是否相等,比较引用数据类型时是地址值是否相等,地址值相等代表引用同一个对象,返回true。 equals(),在Object类中比较的是地址值是否相等,而在其他类重写后,根据重写的equals()方法进行判断。 构造方法能不能重写或者重载 构造器不能被重写,但是能被重载。 基本数据类型 类型 字节 位数 取值范围 默认值 byte 1 8 -128~127 0 short 2 16 -

一条老狗的博客 1507

手把手教你学Simulink——基于Simulink的无位置传感器PMSM滑模观测器(SMO)控制

本文基于Simulink平台实现无位置传感器PMSM滑模观测器(SMO)控制,解决了传统机械位置传感器存在的成本高、体积大、抗干扰差等问题。SMO通过滑模切换函数估算反电动势,进而获得转子位置和速度,具有强鲁棒性(参数变化20%误差仅0.4°)和良好低速性能(50rpm转矩衰减<5%)。文章详细介绍了PMSM数学模型、SMO算法设计、Simulink建模步骤及参数优化方法,提供了可直接工程应用的MATLAB代码和仿真模型,为电动汽车驱动、工业机器人等高精度控制场景提供了有效解决方案。

MHD0815的博客 229

CloudCompare 二次开发(9)——半径滤波

基于PCL对CloudCompare软件进行二次开发,实现的算法功能是半径滤波

点云侠的博客 1401

个人笔记(杂项)

1

weixin_46388053的博客 172

Openclaw错误分析与解决方案之:Agent couldn‘t generate a response. Please try again.

Openclaw错误分析与解决方案:Agent couldn't generate a response. Please try again.

多媒体编程、网络编程、系统编程、网络安全编程、驱动编程 1224

面试汇总-Java基础-杂项(持续更新)

​​​ 除了double和long之外,对其他基本数据类型的操作都是原子性的。而对于double和long,两种类型都是8个字节64位,在32位操作系统的环境下,一次操作只能读取32位,无法一次性读取double和long数据,多线程环境下可能导致数据读写错误。...

weixin_37672801的博客 459

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

java基础数据结构

1.equals 和== equals :底层实现也是调用的== 如果想要只比较值就要重写equals方法 == : 引用数据类型:比较的是地址值 基本数据类型比较的是数值(不一定类型相同2.0==2) String s1 = new String("zs"); String s2 = new String("zs"); System.out.println(s1 == s2); //false 因为都是new的地址不一样 String s3 ...

Culprit的博客 65

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

【JAVA零散知识点】

在Java面试中可能会遇到的一些知识点。

Stdio_QU的博客 211

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

开发工具之Eclipse(三)

1. 2. 3. 4.

Linda的专栏 526

java--面向对象

杂项 1.在一个源文件中,只能有一个公共类,但可有任意数目的非公共类。文件名必须与public类的名字匹配。 2.多个源文件,javac xxx.java,只指定包含main的源文件,会自动编译关联的其他源文件。java xxx,xxx为包含main的源文件不含后缀名。 3.每个类都可有一个main方法用于此类单元测试,运行进程时,采用指定名称类的main作为进程的main。 4.类型 - 基本数据类型 - 对象 ...

x13262608581的博客 625

%I64d 和%lld 的区别

//为了和DSP兼容,TSint64和TUint64设置成TSint40和TUint40一样的数 //结果VC中还是认为是32位的,显然不合适 //typedefsignedlongintTSint64; //typedefunsignedlongintTUint64; //ANSIC中规定longlong才能表示64位 ...

ZLei 3万+

【C++】优先队列

STL之priority_queue【转】 STL之优先队列 原本以为priority_queue很简单,才知道原来懂的只是最简单的形式。 头文件:#include<queue> 优先队列,也就是原来我们学过的堆,按照自己定义的优先级出队时。默认情况下底层是以Vector实现的heap。 既然是队列,也就只有入队、出队、判空、大小的操作,...

ZLei 589

【Java】方法的重写和方法的重载

方法的重写和方法的重载是两个不同的概念: 方法的重载是在同一个类中,定义同名函数,通过参数类型和个数的不同来区分不同的函数, 方法的重写是在父类和子类中, 子类通过定义和父类函数同名的函数可以覆盖父类的方法, 这称为函数的重写 ...

ZLei 569

【JAVA】jave正则表达式

Java正则表达式 一:什么是正则表达式 1.定义:正则表达式是一种可以用于模式匹配和替换的规范,一个正则表达式就是由普通的字符(例如字符a到z)以及特殊字符(元字符)组成的文字模式,它 用以描述在查找文字主体时待匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。 2.用途: 字符串匹配(字符匹配) 字符串查找 字符串替换 字符串...

ZLei 488

【Java】基础、Final 关键字

final 修饰符, 表示不可改变的常量1.final 修饰成员变量的时候初始化的地方有两个, 一个是在定义时候初始化(构造器或者非静态初始化块), 一个是在构建对象时候初始化,只能在一个地方初始化,并且在初始化之后不可以改变。 2.final 修饰类变量的时候可以在定义时候初始化,也可以在静态初始化块中定义初始化 只能在一个地方初始化,并且在初始化之后不可以改变。 3.final 修饰局部变量可以

ZLei 417

【Java】JVM(三)、Java垃圾收集器

一、Minor GC、Major GC 和 Full GC   Minor GC:清理新生代空间,当Eden空间不能分配时候引发Minor GC   Major GC:清理老年代空间   Full GC:清理Java堆空间 二、新生代垃圾收集器   1. Serial 收集器      线程:单线程收集器,   算法:新生代标记复制,老年代标记整理   缺点:在进行垃圾...

ZLei 208

【Java】JVM(四)、虚拟机参数配置

1.-Xms20M JVM启动时候的内存大小为20M 2.-Xmx20M JVM内存最大值是20M 将其与Xms大小一致可以避免JVM内存自动扩展 3. -Xss128K 表示可以设置虚拟机栈大小为128K 4. -Xoss128K 表示设置本地方法栈的大小为128K(HotSpot不区分虚拟机栈和方法栈) 5. -...

ZLei 206

【Java】JavaIO(二)、节点流

一、InputStream & outputStream   Java字节流主要是以InputStream (输入流),outputStream(输出流)为基类,本身是抽象类不能创建实例,但是是字节流的基类,所以方法都可以使用,主要的方法有   InputStream: 1 int read();读取单个字节 2 int read(byte[] b); 最多读取b.length 字...

ZLei 204
上一篇: 【Java】JVM(六)虚拟机字节码执行引擎
下一篇: 【索引】索引的介绍与优化
三生石1103
博客等级 码龄12年 16粉丝 87原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值