重写equals为什么要重写hashCode(配合源码分析)

news2024/12/23 9:02:38

目录

一、hashCode的概念

二、为什么要有hashCode

三、为什么重写equals要重写hashCode

四、HashSet源码分析

五、容易记混的点


一、hashCode的概念

        hashCode()是Object定义的方法,它将返回一个整型值,这个方法通常用来将对象的内存地址转换为整数之后返回,它存在的价值是为Hash容器处理数据时提供支持,Hash容器可以根据hashCode定位需要使用的对象,也可以根据hashCode来排除2个不相同的对象,即:hashCode不同,则视为2个对象不同。

二、为什么要有hashCode

        哈希码主要在哈希表这类集合映射的时候用到,哈希表存储的是键值对(key-value),它的特点是:能根据“键”快速的映射到对应的“值”。这其中就利⽤到了哈希码。
例如 HashMap 怎么把 key 映射到对应的 value 上呢?用的就是哈希取余法,也就是拿哈希码和存储元素的数组的长度取余,获取 key 对应的 value 所在的下标位置。

三、为什么重写equals要重写hashCode

这里我们举例说明:

1、新建一个实体类QueryBO。

import lombok.Data;

/**
 * 查询
 *
 * @author yunyan
 * @date 2023/7/8
 */
@Data
public class QueryBO {

    private Boolean query1;
    private Boolean query2;
    private Boolean query3;
}

测试类:

/**
 * 测试hashCode
 *
 * @author yunyan
 * @date 2023/7/18
 */
public class TestHash {
    public static void main(String[] args) {
        QueryBO queryBO1=new QueryBO();
        queryBO1.setQuery1(true);
        queryBO1.setQuery2(true);
        queryBO1.setQuery3(false);

        QueryBO queryBO2=new QueryBO();
        queryBO2.setQuery1(false);
        queryBO2.setQuery2(false);
        queryBO2.setQuery3(false);

        System.out.println(queryBO1.equals(queryBO2));
    }
}

如果是原有的equals和hashCode方法,可以看出,如果没有重写equals方法,那么两个对象进行equals比较时,只有当两个对象的所有属性都相等时,才会认为两个对象相等。

结果:

 2、在QueryBO类里只重写equals方法

import lombok.Data;

import java.util.Objects;

/**
 * 查询
 *
 * @author yunyan
 * @date 2023/7/8
 */
@Data
public class QueryBO {

    private Boolean query1;
    private Boolean query2;
    private Boolean query3;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        QueryBO queryBO = (QueryBO) o;
        return Objects.equals(query3, queryBO.query3);
    }
}

结果:现在只要两个对象的query3属性相同,那么就认为两个对象相同

 3、继续测试,现在我使用HashSet集合将两个对象添加进去

import java.util.HashSet;
import java.util.Set;

/**
 * 测试hashCode
 *
 * @author yunyan
 * @date 2023/7/18
 */
public class TestHash {
    public static void main(String[] args) {
        QueryBO queryBO1=new QueryBO();
        queryBO1.setQuery1(true);
        queryBO1.setQuery2(true);
        queryBO1.setQuery3(false);

        QueryBO queryBO2=new QueryBO();
        queryBO2.setQuery1(false);
        queryBO2.setQuery2(false);
        queryBO2.setQuery3(false);

        System.out.println(queryBO1.equals(queryBO2));

        Set<QueryBO> set=new HashSet<>();
        set.add(queryBO1);
        set.add(queryBO2);
        System.out.println(set);
    }
}

结果是:

 这里我们都知道set集合是去重的,明明我们的两个对象都相等,但是set集合却认为是两个不同的对象。

原因是: HashSet、HashMap集合在添加元素的时候,用哈希取余法,也就是拿hashCode和存储元素的数组的长度取余,获取 key 对应的 value 所在的下标位置。如果获取到的下标位置上已经存在元素,则认为产生了哈希碰撞(hashCode() 所使用的散列算法也许刚好会让多个对象传回相同的散列值。)再拿新添元素(新的)与当前位置上的元素(旧的)进行equals比较,如果返回true就用新的替换旧的。如果返回false就添加到该位置下面的链表尾或红黑树上。而如果没有发生哈希碰撞,也就间接说明两个对象不相等。然而我们上面只是重写了equals方法(只比较query3是否相同),而hashCode返回的还是三个query属性组合成的数组的hashCode,所以虽然equals返回true,但是两个对象的hashCode不一样,set里存在两个QueryBO对象而不是一个。

现在在实体类里重写hashCode方法

@Override
    public int hashCode() {
        return Objects.hash(query3);
    }

看一下程序的运行结果:

 这就是为什么重写equals方法必须要重写hashCode方法的原因。

四、HashSet源码分析

下面我们查看HashSet的源码来分析情况

1、首先我们调用了HashSet的构造方法

 他的内容是创建一个新的HashMap

2、 原来HashSet是借助HashMap实现的,我们来看看他的add方法,通过idea中的Structure可以看到类的结构。

 3、到这里我们可以发现,这个add方法,调用的也是map的put方法。以传入的元素作为Key,PRESENT作为Value,而PRESENT其实是一个空对象。

 4、下面来看看put方法

 从这里可以看出来,HashMap对于节点的判断其实是利用了hash函数,而这个hash函数是借助了传入的Key的Hash函数

所以说,如果我们只重写了equals没有重写hashcode,就会发生与期望结果不相符的情况。

五、容易记混的点

  1. hashCode相等,两个字符串不一定相等。
  2. hashCode相等,这两个对象不一定相等。
  3. hashCode不等,这两个对象一定不相等。
  4. 如果两个对象分别调⽤ equals 方法都返回 true,则这两个对象一定相等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/767414.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

postman批量执行请求,通过json传参

1、创建请求 "authUid":"{{authUid}}", 加粗为需要替换的参数 2、组装JSON 可通过Excel自动填充功能构造数据 [ {"authUid":"18700000001"}, {"authUid":"18725929202"} ] 3、批量执行请求配置 4、然后start r…

「网络编程」传输层协议_ UDP协议学习_及原理深入理解

「前言」文章内容大致是传输层协议&#xff0c;UDP协议讲解。 「归属专栏」网络编程 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、传输层二、UDP协议2.1 再谈端口号2.2.1 端口号范围划分2.2.2 认识知名端口号2.2.3 端口号注意问题2.2.4 netstat命令和pidof命令 2.2 UD…

实时数据集成的完美搭档:CDC技术与Kafka集成的解决方案

目录 一、实时数据同步 二、可靠的数据传输 三、灵活的数据处理 四、解耦数据系统 五、主流免费CDC工具介绍 六、Flink CDC安装使用步骤&#xff1a; 七、ETL CDC安装使用步骤 八、写在最后 一、实时数据同步 随着企业数据不断增长&#xff0c;如何高效地捕获、同步和…

如何将视频音频提取出来?这几个方法真的实用!

在视频制作和后期处理中&#xff0c;有时我们需要将视频中的音频提取出来&#xff0c;以便单独处理&#xff0c;如剪辑、增强声音等&#xff0c;而不影响视频内容。为了帮助您实现这一目标&#xff0c;下面将介绍几种常用的方法来提取视频音频。 方法一&#xff1a;记灵在线工…

SSH服务(二十六)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、概述 二、特点 三、身份验证机制 四、验证过程 五、加密机制 六、基本参数 ​七、 身份验证机制 八、基本操作 1. ssh 2. scp 3. sftp 4. 密钥对验证 总结 前…

vue或react当中canvas实现电子签名组件和使用canvas进行图片压缩

<template><div><h1>vue3</h1><canvas id"canvasWrite"> 浏览器不支持Canvas,请升级浏览器 </canvas><div><button class"submit" click"submitWrite">提交签名</button><button clas…

DOM学习

1.DOM 简介 1.1 什么是 DOM 文档对象模型&#xff08;Document Object Model&#xff0c;简称 DOM&#xff09;&#xff0c;是 W3C 组织推荐的处理可扩展标记语言&#xff08;HTML 或者XML&#xff09;的标准编程接口。 W3C 已经定义了一系列的 DOM 接口&#xff0c;通过这些…

Spring项目创建与Bean的存储与读取(DL)

文章目录 一. Spring项目的创建1. 创建Maven项目2. 配置国内源3. 添加spring依赖4. 创建启动类 二.储存或读取Bean对象1. 添加spring配置文件2. 创建Bean对象3. 读取Bean对象 一. Spring项目的创建 1. 创建Maven项目 第一步&#xff0c;创建 Maven 项目&#xff0c;Spring 也…

LAZYSYSADMIN1靶机详解

LAZYSYSADMIN1靶机复盘 一个五分钟就能结束的靶机&#xff0c;非常快。 下载地址&#xff1a;https://download.vulnhub.com/lazysysadmin/Lazysysadmin.zip nmap对这个ip进行单独扫描后发现开启的服务挺多。 就直接枚举了一下这个服务器的内容&#xff0c;发现了一个用户名…

试验数据管理平台解决方案-MDM

试验是汽车研发、生产制造和售后保障过程中必不可少的重要环节。试验员曾到过寒区、热区、高原、沙漠和山路等恶劣环境下的场地来对试验车辆进行相关试验。这是因为汽车在不同的道路、地理和气候条件下行驶时&#xff0c;其性能、效率、可靠性和耐久性等技术特性也会发生变化。…

2023四大进销存软件推荐,第一款适合中小商户使用!

在当今竞争激烈的商业环境中&#xff0c;企业或商户迫切需要使用进销存管理软件&#xff0c;来优化库存控制、提高运营效率、降低成本&#xff0c;并加强与供应链合作伙伴之间的协作和沟通。 为了帮助企业选择适合自己需求的进销存软件&#xff0c;我们特别整理了2023年市面上比…

选择适合的接口测试工具,助力高效在线接口测试

选择适合的接口测试工具&#xff0c;助力高效在线接口测试 接口测试工具在软件开发中扮演着重要的角色。本文将重点介绍不同类型的接口测试工具&#xff0c;特别是在线接口测试工具&#xff0c;探讨其优势和适用场景&#xff0c;帮助您选择适合的工具&#xff0c;提高接口测试…

4、深入理解ribbon

一、负载均衡的两种方式 服务器端负载均衡 传统的方式前端发送请求会到我们的的nginx上去&#xff0c;nginx作为反向代理&#xff0c;然后路由给后端的服务器&#xff0c;由于负载均衡算法是nginx提供的&#xff0c;而nginx是部署到服务器端的&#xff0c;所以这种方式又被称为…

【Linux】多线程(二)

文章目录 生产者消费者模型为何要使用生产者消费者模型生产者消费者模型优点基于BlockingQueue的生产者消费者模型条件变量条件变量代码 POSIX信号量基于环形队列的生产消费模型 生产者消费者模型 为何要使用生产者消费者模型 生产者消费者模式就是通过一个容器来解决生产者和…

qt与opencv学习记录

qtopencv开发入门&#xff1a;4步搞定环境配置-1_哔哩哔哩_bilibili qtopencv开发入门&#xff1a;4步搞定opencv环境配置2_哔哩哔哩_bilibili 文章内容来自上面两个视频&#xff0c;感谢创作者。 ps&#xff1a;配置环境的过程中&#xff0c;遇到了很多问题&#xff0c;我…

UML 图

统一建模语言&#xff08;Unified Modeling Language&#xff0c;UML&#xff09;是用来设计软件的可视化建模语言。它的特点是简单、统一、图形化、能表达软件设计中的动态与静态信息。 UML 从目标系统的不同角度出发&#xff0c;定义了用例图、类图、对象图、状态图、活动图…

网络流量监控分析

网络管理员是维护健全网络基础设施的关键&#xff0c;这通常是一项艰巨的任务&#xff0c;因为管理员需要 24x7 全天候监控和管理网络和服务器。但是&#xff0c;即使进行全天候监控&#xff0c;每个网络也容易受到带宽占用的影响&#xff0c;如果导致关键业务应用程序变慢&…

CSS 实现 Turbo 官网 3D 网格线背景动画

转载请注明出处&#xff0c;点击此处 查看更多精彩内容 查看 Turbo 官网 时发现它的背景动画挺有意思&#xff0c;就自己动手实现了一下。下面对关键点进行解释说明&#xff0c;查看完整代码及预览效果请 点击这里。 简单说明原理&#xff1a;使用 mask-image 遮罩绘制网格&a…

二叉树--C语言实现数据结构

本期带大家一起用C语言实现二叉树&#x1f308;&#x1f308;&#x1f308; 1、二叉树的定义 二叉树是一种特殊的树状数据结构&#xff0c;它由节点组成&#xff0c;每个节点最多有两个子节点&#xff0c;分别称为左子节点和右子节点 二叉树的链式存储结构是指用 链表 来表示…

公司私服Maven踩坑,项目配置都OK但是包就是下载不下来【已解决】

我的问题是公司的私服Maven下载不下来&#xff0c;因为公司保密协议&#xff0c;这里用阿里云为例&#xff01; 具体的至少参考&#xff1a;(32条消息) 这篇博文只讲MirrorOf_Java软件工程师的博客-CSDN博客 1&#xff1a;Java的Maven爆红了就找到资源库&#xff0c;然后把对于…