hashCode equal避免的几个误区

本文探讨了equals方法和hashcode方法的特性及其在集合类中的应用,强调了两者一致性和重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[size=medium]对于hashcode方法和equals方法,我们需要注意以下几点:
1.equals方法所需要的几个特性
?什么情况下进行equals的比较?答案是用equals比较的只是值本身,对于equals方法,读者需要记住按照两种情况来运用,一比较基本类型的值的比较,二比较的是对象。equals比较的只是值或者对象的内容。
非空的x,y,z,
一致性:无论x.equals(y)比较多少次,都应该返回true
反射性:任何对象和它本身比较都应该返回true
类推性:x.equals(y), y.equals(z),x.equals(z)都应该返回true
对称性:x.equals(y)和y.equals(x)返回的应该都是true.
非空和null比较永久都该返回false.
2.hashcode和equals的关系
对于java中的object类来说,hashcode返回的是对象的内存地址(可以粗略的这样认为),String类(以及其他原生数据类型如int ,flat等的引用数据类型Integer Float等)重写了object的hashcode和equals方法,但是在这里注意,不要被基本数据类型的对于hashcode和equals方法重写所迷惑了。基本数据类型对于OBJECT的该2个方法的重写并不意味着你再现实的代码中也同样照做(下面会解释).基本类型String等对于这两个方法的重写其实着重在内容上。
Java定义的规则中硬性的绑定了2个原则:
hashcode值相同的2个对象(注意这里是对象)并不一定是同一个对象,也就是说equals方法返回的不一定是true,即对象的内容可能不同。
equals方法返回true,hashcode的返回的int类型的值并不一定相同,即内容相同的两个对象不一定是同一个对象(名字相同的人不一定是同一个人)。
相同对象必定equals方法返回true且hashcode值相同

3.重点关键所在:现实当中重写equals方法必定要重写hashcode?
读者可以直接读取java的目录中的rt.jar,找到string类的源码,可以发现,String的hashcode方法是读取字符串中的每个字符,并不是对象的内存地址,换句话来说,object的hashcode方法在string中被重写,equals方法也是对2个字符串内容进行比较,hashcode也被定义成与内容相关,[b]所以我们可以发现为了保证基本类型对象是同一个对象:必须让hashcode返回值和equals方法都绑定到对于内容的处理上,换句话来说,满足了上面第二点的分析,同一个对象hashcode,equals2个方法都必须满足[/b]。
现实当中,只有在用到集合类的时候,才需要重写hashcode方法,否则不需要重写hashcode方法,比较2个对象只需要equals方法即可,当用到集合类时,重写hashcode方法并不是效率的考虑,而是必须重写hashcode方法,否则会出现宏观上的理解错误:
mport java.util.*;
public class HashSetTest
{
public static void main(String[] args)
{
HashSet hs=new HashSet();
hs.add(new Student(1,"t1"));
hs.add(new Student(2,"t2"));
hs.add(new Student(3,"t3"));
hs.add(new Student(1,"t1"));

Iterator it=hs.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
}
}
class Student
{
int num;
String name;
Student(int num,String name)
{
this.num=num;
this.name=name;
}
public String toString()
{
return num+":"+name;
}
}
输出结果为:
1:t1
1:t1
3:t3
2:t2

可见这样的效果并不是我们从宏观上想要的,对于我们自己定义的对象student来说,我们需要num,name相同的对象就应当是同一个对象,但是由于没有重写equals方法和hashcode方法,使用new 运算符创建对象时,内存地址是不同的,因为沿用了object的hashcode和equals方法,导致set对传入的对象的判断上发生错误,导致我们重复塞了2个“相同的对象”(这种相同是指我们自己定义的规则,比如学号相同,名称相同我们就认为是同一个对象)。


读者可以查看HashSet的源码,可以发现HashSet底层采用HashMap实现,HashMap并不允许重复的K值存入,所以当存入新的K时,会对传入的参数K的hashCode值进行判断,如果K相同,那么覆盖老的V,用新的V:
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
我们可以发现这里双重调用到了hash和equals,所以为了避免存入重复对象,我们需要重写我们自己的student类的hashcode和equals方法,以告诉hashSet避免存入我们宏观意义上的重复对象。
而对于String等基本类型来说,比如
HashSet hs=new HashSet();
hs.put("1","2");
hs.put("1","3"); //第二步
当进行put时,代码第二步处会对传入的"1"(K)进行判断,因为已经存在一个K的hashcode以及equals方法和第二步处传入的"1"相同,所以视作同一个K处理。


最后总结一点:一般项目中如果需要用到集合类如hashtable,hashset时,那么我们自己新建的对象如果需要存入该集合类,那么必须重写hashcode以及equals方法,不能漏掉任何一个。
[/size]
### Java 中 `hashCode` 和 `equals` 方法的区别及作用 #### 区别 - **定义与用途** - `hashCode()` 方法返回对象的哈希码值,主要用于基于哈希表的数据结构(如 HashMap 或 HashSet),以便快速定位数据的位置[^1]。而 `equals(Object obj)` 方法用来判断两个对象是否相等,通常通过比较成员变量的内容实现逻辑上的相等性检查[^5]。 - **返回类型** - `hashCode()` 返回的是 int 类型数值;`equals()` 则是一个布尔类型的函数,当调用者和参数代表同一个实体时返回 true 否则 false[^3]。 - **默认行为** - 对于来自 Object 类的对象,默认情况下 `hashCode()` 的计算依据是内存地址转换成整数形式表示;对于 `equals()` 来说,在未覆盖之前它仅能做引用级别的对比即只有完全相同的实例才会被认为是相等的[^4]。 #### 协作机制 为了确保在集合框架内能够正常工作,特别是像 HashMap 这样的容器类,每当自定义了一个新的 class 并打算将其作为 key 使用的时候,应该总是同时覆写这两个方法: - 当两个对象根据业务需求认为彼此“相等”,那么它们必须拥有相同的 hash code 值; - 反之如果两者的 hash codes 不同,则可以直接断定二者必定不是同一项记录无需再深入考察其属性层面的一致性问题了[^2]。 因此良好的实践是在每次修改影响到对象身份验证的关键字段之后都要重新评估并调整相应的 `hashCode()` 实现方式以保持一致性。 ```java @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof MyClass)) return false; MyClass myClass = (MyClass) o; // Compare relevant fields here... } @Override public int hashCode() { int result = fieldA.hashCode(); result = 31 * result + fieldB.hashCode(); ... return result; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值