Java - 提高(8) - equals和hashCode

HashMap底层实现原理解析之put()、get()方法原理 阅读详情

equals和hashCode



HashCode的作用

Object的源码中,hashCode是这样定义的:

public native int hashCode();
JDK API中对HashCode的描述:
返回该对象的哈希码值。支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。
实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。

(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)


当我们向一个集合中添加某个元素,集合会首先调用hashCode方法,这样就可以直接定位它所存储的位置,若该处没有其他元素,则直接保存。
若该处已经有元素存在,就调用equals方法来匹配这两个元素是否相同,相同则不存,不同则散列到其他位置(可以参考HashMap)
这样处理,当我们存入大量元素时就可以大大减少调用equals()方法的次数,极大地提高了效率。

所以hashCode在上面扮演的角色为寻域(寻找某个对象在集合中区域位置)。
hashCode可以将集合分成若干个区域,每个对象都可以计算出他们的hash码,可以将hash码分组,每个分组对应着某个存储区域,
根据一个对象的hash码就可以确定该对象所存储区域,这样就大大减少查询匹配元素的数量,提高了查询效率。



HashCode与equals

两个对象的equals相等,这两个对象的hashCode必定相等!
两个对象的equals不相等,这两个对象的hashCode可能相等,可能不相等!



equlas()

超类Object中equals()方法,该方法主要用于比较两个对象是否相等,该方法的源码如下:

我们知道所有的对象都拥有标识(内存地址)和状态(数据),同时"=="是比较两个对象的内存地址,

所以说使用Object的equals()方法是比较两个对象的内存地址是否相同,即:object1.equals(object2)为true,则表示object1和object2实际上是引用了同一个对象。


实际上JDK中,String,Math等封装类都对equals()方法进行了重写。


String的equals()方法:

public boolean equals(Object anObject) {
	if (this == anObject) {
			return true;
	}
	if (anObject instanceof String) {
			String anotherString = (String)anObject;
			int n = count;
			if (n == anotherString.count) {
					char v1[] = value;
					char v2[] = anotherString.value;
					int i = offset;
					int j = anotherString.offset;
					while (n-- != 0) {
							if (v1[i++] != v2[j++])
									return false;
					}
					return true;
			}
	}
	return false;
}

对于这个代码段:if (v1[i++] != v2[j++])   return false; 

可以看出String的eqauls()方法是进行内容比较,而不是引用比较,

至于其他的封装都差不多。



在equals()方法中使用getClass进行类型判断

我们复写equals()方法时,一般都是推荐使用getClass来进行类型判断,不推荐instanceof。

因为instanceof的作用是判断其左边对象是否为其右边对象的实例,返回boolean类型的数据。

可以用来判断继承中子类的实例是否为父类的实现。


注意这句话:"可以用来判 断继承中的子类的实例是否为父类的实现",正是这句话在作怪

我们来看一个栗子:

/**
* 父类
*/
public class Person {
	public Person(String name) {
			this.name = name;
	}
	/**
	 * 重写equals方法
	 */
	@Override
	public boolean equals(Object obj) {

	if (obj instanceof Person) {
			Person person = (Person) obj;
			if (person.getName() == null || name == null) {
					return false;
			} else {
					return name.equalsIgnoreCase(person.getName());// 将此String与另一个String比较,不考虑大小写。
			}
	}
	return false;
	}
	protected String name;
	public String getName() {
			return name;
	}
	public void setName(String name) {
			this.name = name;
	}
}

/**
* 子类
*/
public class Employee extends Person {
	private int id;
	public Employee(String name, int id) {
			super(name);
			this.id = id;
	}
	/**
	 * 重写equals方法
	 */
	@Override
	public boolean equals(Object obj) {
			if (obj instanceof Employee) {
					Employee emp = (Employee) obj;
					return super.equals(obj) && emp.getId() == id;
			}
			return false;
	}
	public int getId() {
			return id;
	}
	public void setId(int id) {
			this.id = id;
	}
}

public class Test {
	/**
	 * 上面父类Person和子类Employee都重写了equals()方法,不过Employee比父类多了一个id属性.
	 * 
	 * @param args
	 */
	public static void main(String[] args) {

	Employee e1 = new Employee("chenssy", 23);
	Employee e2 = new Employee("chenssy", 25);
	Person p1 = new Person("chenssy");
	
	System.out.println(p1.equals(e1));
	System.out.println(p1.equals(e2));
	System.out.println(e1.equals(e2));
	}
}

输出:true,true,false


上面定义了两个员工和一个普通人,虽然他们同名,但是他们肯定不是同一人,所以按理说结果应该全部是false,和现在的结果对不上。


对于 "e1 != e2" 很好理解,因为他们不仅需要比较name还需要比较id,只是 p1即等于e1也等于e2,这是非常奇怪的,

因为e1 和e2明明是两个不同的类,为什么会出现这种情况呢?


首先p1.equals(e1),是调用p1的equals方法,该方法使用instanceof关键字来检查e1是否为Person类

然后看instanceof:判断其左边对象是否为其右边类的实例,也可以用来判断继承中子类的实例是否为父类的实现。

他们两者存在继承关系,所以结果为true,接着往下走,而两者name又相同,所以结果肯定是true。


所以出现上面的情况就是使用了关键字instanceof,很容易出现bug,所以在重写equals方法时推荐getClass进行类型判断,而不是instanceof。



参考资料:

http://www.cnblogs.com/chenssy/ 

《Thinking in Java》

一文搞懂蚁群算法Ant Colony Optimization(ACO)!!!(灵感起源+数学原理+代码实现+实战应用) 本文系统介绍了蚁群算法的原理与实现。首先从蚂蚁觅食行为出发,阐释了信息素正反馈机制如何引导群体找到最短路径。随后详细解析了算法的数学模型,包括状态转移规则和信息素更新机制。通过MATLAB代码演示了算法在旅行商问题(TSP)中的应用,展示了路径构建和优化的完整流程。最后讨论了算法的优势、局限性和实际应用场景。蚁群算法通过模拟生物智能,为解决复杂优化问题提供了一种高效方法,其核心在于信息素的正反馈机制与随机探索的平衡。 阅读详情

相关推荐

车载毫米波雷达实战:用MATLAB复现DDMA-MIMO雷达的Empty-band算法(附代码解析)

本文详细介绍了车载毫米波雷达中DDMA-MIMO技术的实战应用,重点解析了Empty-band算法的原理与MATLAB实现。通过搭建仿真环境,文章深入探讨了发射天线通道解调与相位法速度解模糊两大核心难题的解决方案,并提供了完整的代码解析,为雷达算法工程师提供了从理论到实践的清晰指南。

hill8的博客 559

Java性能优化(8):改写equals时总是要改写hashCode

一个很常见的错误源于没有改写hashCode方法。在每个改写了equals方法的类中,你必须也要改写hashCode方法。如果不这样做到话,就会违反Object.hashCode的通用约定,从而导致该类无法与所有基于散列值(hash)的集合类结合在一起正常运作,这样的集合类包括hashMap、HashSet和Hashtable。 下面是hashcode约定的内容,来自ja...

weixin_30840573的博客 353

别再为STM32F407的USB高速通信发愁了!手把手教你用USB3320(ULPI接口)搞定硬件设计

本文详细介绍了STM32F407通过USB3320(ULPI接口)实现高速USB通信的硬件设计实战。从USB3320的选型优势到原理图设计避坑,再到PCB布局和固件配置,提供了一套完整的解决方案。特别针对USB PHY芯片的常见问题,如供电处理、信号完整性和初始化代码,给出了专业建议和调试技巧,帮助工程师高效完成高速USB通信设计。

weixin_42527401的博客 388

java核心技术学习笔记8---equals()方法和hashCode()方法

equals方法 Object类中的equals方法用于检测一个对象是否等于另一个对象,即判断两个对象是否具有相同的引用。然而,经常需要检测两个对象状态的相等性。如果两个对象的状态相等,就认为两个对象是相等的。我们可以根据自己的需求重写equals方法。 Java语言规范要求equals方法具有闭包特性。 1)自反性:对于任何非空引用x,x.equals(x)应该返回true。 2)对称性

busy50的专栏 576

手把手教你用Ubuntu Base 20.04打造轻量级ARM开发板根文件系统(附常见问题解决)

本文详细介绍了如何基于Ubuntu Base 20.04 LTS构建轻量级ARM开发板根文件系统,涵盖环境准备、镜像制作、跨架构配置、软件包定制及常见问题解决。通过实战指南,开发者可快速掌握ARM架构下的根文件系统构建技巧,优化系统性能并解决部署中的典型问题。

gin88的博客 211

将ue4程序嵌入qt界面显示

需求:将ue4程序嵌入qt界面显示 思路:通过使用 windowsAPI 对ue4窗口进行设置跟随qt界面以及活动窗口时的位置层序设置

weixin_44342705的博客 5844

day21—选择题

二叉树的结点个数 = 叶子结点的个数 + 度为1的结点的个数 + 度为2的结点的个数;思路:希尔排序是直接插入排序的优化,它的时间复杂度和函数有关,大概是在O(N ^ 1.3)—O(N ^ 1.5);思路:AVL数是平衡二叉搜索树,左右子树的高度差小于等于 1并且每一棵子树也是一棵平衡二叉搜索树,要保证左树小于根结点,右树大于根结点;思路:时间复杂度考虑最坏情况:原单链表为2->3->4->5,现在要求插入1,如果采用尾插法,插入后的结果就是2->3->4->5->1,就需要把1向前移动N次。

m0_61963422的博客 507

使用STC15F104W产生PWM脉冲

最近的小项目中使用到了STC15F104W产生PWM脉冲

yeats1989的专栏 1万+

java8 stream api对集合进行去重(distinct),记得覆写实体类的equals和hashCode方法

没有覆写实体类方法时,去重失败 实体类 package com.ahut.common.entity; import java.util.Objects; public class BaseBean { private String name; private int age; public BaseBean(String name, int age) { ...

大白能的博客 5260

STM32 (基于HAL库) 硬件IIC任意地址读写任意字节----AT24CXX芯片

转了一圈,发现有关STM32的iic的硬件是使用比较少,基于HAL库的就更少,传统的,大家都是采用软件模拟的。有iic的协议相对复杂,同时灵活,尤其是不同的设备的I2C通信组合变化也不同,使用灵活的软件模拟也是可以理解的,在加上I2C的设备读写速度都比较低,硬件读写的优势就更无法体现,哪怕硬件I2C可以开启DMA,甚至在随机的读取少量字节的情况下。开启DMA反而效果更差。自然而然的硬件I2C就无人问津了。

qq_32273059的博客 1580

糖果【贪心】

【问题描述】 有N(1≤N <100,000)N(1≤N~<100,000)N(1≤N <100,000)种不同类型的糖果,每种糖果数量无限多。第i种糖果每颗的价格是Pi(1≤Pi≤1018),有Ci(1≤Ci≤1018)P_i(1≤P_i≤10^{18}),有C_i (1≤C_i≤10^{18)}Pi​(1≤Pi​≤1018),有Ci​(1≤Ci​≤1018)头牛想吃这种糖果。 FarmerJohnFarmer JohnFarmerJohn拥有金钱BBB元(1≤B≤1

算法小猪的博客 795

【Java面试】为什么重写equals方法必须同时重写HashCode方法?

对于散列集合,他们使用的是hashcode来判断是否是相同的,如果存储两个相同的对象,但是他们的hashcode值不同,那么就会导致这两个对象存储在hash表的两个不同的位置,然后当我们想要获取数据的时候,就会出现一个完全相同的对象,会存储在hash表的两个位置,就会破坏程序开发过程中我们约定俗成的规则,就会出现一些不可预料的错误。而如果重写了equals方法,但是没有重写hashCode方法,那么就会导致hashcode值可能不一样,而如果出现这种情况,就会导致这个类无法和所有的集合类一起工作。

Zhangsama1的博客 1040

Lammps实现结冰匀相成核

Lammps实现结冰匀相成核

Rockyoung 3846

Java JDK8 HashMap简介

Java中的HashMap的实现原理 hashCode 的作用 hashCode的存在主要是用于查找快捷性,如HashTable,HashMap等。hashCode是用来散列存储结构中确定对象的存储地址。 如果两个对象相同,就适用于equals(java.lang.Object)方法,那么这个两个对象的hashCode一定要相同。 如果对象的equals方法被重写,那么对象的hashCode...

Welocme 474

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Java 8中 Objects 类源码实现与分析

Objects类位于java.util包下,自 JDK 1.7 版本新增的,它是一个类,不能被继承,且构造函数是的,不能被实例化,它提供了一系列操作Object对象的静态方法,通常会被当做工具类去使用,其类定义如下:Objects类在jdk源码中应用很广泛,通过搜索一下在rt.jar包中的应用,部分截图如下::::从上面的搜索结果可以看出,在jdk 1.8版本中,大概有366处使用了Objects类的相关方法,下面结合具体的源码来分析一下。...

AS011x的博客 844

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

java objects知道_java.util.Objects分析讲解与调试

今天在研究HashMap在1.8中的源码改动时,随手F3了链表节点Node类的equals方法里的equals 忽然发现,咦,好奇怪,怎么工程里打开了两个Object, 仔细一看才发现一个是Object,一个是Objects,差一个s。所在的包也不一样,一个是java.lang.Object,一个是java.util.Objects。再一看这个Objects类在1.7就已经加入了,有点汗颜,作为一...

weixin_34746715的博客 267

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Set集合和其子类HashSet、LinkedHashSet

🥩HashSet集合介绍 🥓HashSet集合存储数据的结构(哈希表) 🍔什么是哈希表呢? 🍟HashSet存储自定义类型元素 🍕LinkedHashSet

write less , do more 461

List 如何根据对象的属性去重?Java 8 轻松搞定!

欢迎关注方志朋的博客,回复”666“获面试宝典一、去除List中重复的StringpublicList<String>removeStringListDupli(List<String>stringList){ Set<String>set=newLinkedHashSet<>(); set....

方志朋的博客 635

java集合HashSet

java集合hashSet

小白有个大牛梦 273

hash c语言实现原理,GitHub - nlifew/hashmap: c实现的原理性hashmap(HashTable+RBTree)

前言作为一名 java 程序猿,java.util 和 java.lang 包下的源码是必须要看,而且是必须要看懂的。前者包含了许多在工程中经常使用的类,如 ArrayList,LinkedList,HashMap,LinkedHashMap等。 List 即线性表,这部分比较简单,网上也有很多这部分的源码解析。Map 用来保存键值对的映射关系,相对比较复杂,但也绝对不是天书那种难度的。看大佬的代...

weixin_42507981的博客 399

JAVA-基础知识-HashMap

JAVA-基础知识-HashMap 概述: HashMap最早出现在JDK1.2中,底层基于散列算法实现。HashMap 允许 null 键和 null 值,是非线程安全类,在多线程环境下可能会存在问题。 采取懒创建的方式 new HashMap();完成后,如果没有put操作,是不会分配存储空间的。 当链表长度大于8时链表将转换成红黑树 当节点个数少于6时红黑树转换成链表 原因: 红黑...

横行的螃蟹的博客 237

hashmap原理_java中HashMap的底层实现原理

一,你知道HashMap的工作原理吗?通过hash的方法,通过put和get存储和获取对象。存储对象时,我们将K/V传给put方法时,它调用hashCode计算hash从而得到bucket位置,进一步存储,HashMap会根据当前bucket的占用情况自动调整容量(超过Load Facotr则resize为原来的2倍)。获取对象时,我们将K传给get,它调用hashCode计算hash从而得到bu...

weixin_39596668的博客 134

Map源码解析

Java7 HashMap HashSet 和 HashMap 在 Java 里有着相同的实现,前者仅是对后者进行了一层包装,也就是说 HashSet 里面有一个 HashMap(适配器模式)。 HashMap 实现了 Map 接口,即允许放入 key 为 null 的元素,也允许插入 value 为 null 的元素;除该类未实现同步外,其余跟 Hashtable 大致相同;跟 TreeMap 不同,该容器不保证元素顺序,根据需要该容器可能会对元素重新哈希,元素的顺序也会被重新打散,因此不同时间迭代同一个

qq_51102530的博客 568

Java集合

Java7 中使用 Entry 来代表每个 HashMap 中的数据节点,Java8 中使用 Node,基本没有区别,都是 key,value,hash 和 next 这四个属性,不过,Node 只能用于链表的情况,红黑树的情况需要使用 TreeNode。根据 Java7 HashMap 的介绍,我们知道,查找的时候,根据 hash 值我们能够快速定位到数组的具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表的长度,为 O(n)。其余方法大都是线性时间。

Willowkey的博客 203

Java集合——Set接口

Java集合 Set接口

qq_72836797的博客 379

普歌+浅谈Java中Collection集合(2)

浅谈Java中Collection集合(2)Set接口哈希值HashSet集合HashSet不允许存储重复元素的原理:HashSet存储是无序的原理: Set接口 set接口是Collection接口中的另一个分支。也是单列集合。 Set集合的特点: 不允许存储重复元素 没有索引,没有带索引的方法。 不能使用普通for循环进行遍历 Set继承了很多的接口。后文会提到! 哈希值 我们在深入学习Set集合中具体的HashSet集合和LinkedHashSet集合需要先学习一个哈希值的概念。 哈希值:是

weixin_54899299的博客 691
上一篇: Java - 提高(7) - 内部类
下一篇: 并发基础_1_序
Simba_cheng
博客等级 码龄11年 250粉丝 203原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值