当前位置:网站首页>重写equals为什么要重写hashcode
重写equals为什么要重写hashcode
2022-07-16 14:06:00 【罗罗的1024】
重写equals为什么要重写hashcode?
前提
先了解下object 类
public class Object {
// 比较内存地址是否相等
public boolean equals(Object obj) {
return (this == obj);
}
//把对象的内部地址转换成一个整数,也就是hash值
public native int hashCode();
//........省略部分代码.............
}
实例
假设在业务上,我们认为名字相同的便是同一个人
public class Student {
private String name;
private Integer age;
// ....省略getter和 setter方法....
@Override
public boolean equals(Object o) {
//我们认为名字相同的便是同一个人
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name);
}
}
猜想
如果不重写 hashcode 会发生什么
public static void main(String[] args) {
Map<Object, Object> map = new HashMap<>(4);
Student student1 = new Student("zhangsan", 11);
Student student2 = new Student("zhangsan", 22);
map.put(student1,student1);
map.put(student2,student2);
System.out.println(map.size());
}
下面看看map的put操作发生了什么?
//hashmap的hash算法
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
//1、先比较key的hash值是否相等,再比较key的内存地址是否相等 或者 key的 equals 是否相等
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//2、hash不相等,再比较是不是TreeNode类型
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
//3、链表是否存在相等节点
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
//当前链表的下一个节点是空的
p.next = newNode(hash, key, value, null);//添加新节点
if (binCount >= TREEIFY_THRESHOLD - 1) // 链表长度大于等于 7 ,树化,退出循环
treeifyBin(tab, hash);
break;
}
//下一个节点是否要添加的节点相等,是退出循环
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
//继续往链表后面走
p = e;
}
}
//如果链表中存在相同节点,返回旧值
if (e != null) {
// existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
我们发现当有冲突时,判断的顺序如下
- 先比较key的hash值是否相等,再比较key的内存地址是否相等 或者 key的 equals 是否相等
- hash不相等,再比较是不是TreeNode类型
- 链表是否存在相等节点
由于我们认为名字相同的便是同一个人
当 put 的时候,先比较 student1 和 student2 的 hash 值,由于 student1 和 student2 是不同的对象,他们的 hashcode 方法是调用的 object.hashcode 方法,比较的是对象的地址,很明显,下面这个方法算出来的hash是不相等的
//hashmap的hash算法
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
hash值不相等,也不是TreeNode类型,添加进链表
矛盾了
可是现在名字相同,但是在map中却存在两个人,很明显违背了我们的初衷
很明显,矛盾产生的原因就是调用了 object.hashcode 方法,计算出了不相等的hash值,如果hash值相等,便能满足下面的 if 语句
//1、先比较key的hash值是否相等,再比较key的内存地址是否相等 或者 key的 equals 是否相等
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
e = p;
于是乎,我想设计使得名字相同的 student 产生相同的hash值
重写hashcode
我们知道不同的值可能会产生相同的hash值,但是在map中这个问题是不存在的,因为当hash值相等时,还会比较它的 equals方法,但是我们也应该尽量避免hash的碰撞,毕竟hash碰撞对性能还是有点影响的
@Override
public int hashCode() {
return Objects.hash(name);
}
综上,重写equals要重写hashcode就是为了避免像在map这样的数据结构中,禁止同一个key存在多个value值。
结束!!!
边栏推荐
猜你喜欢

Halcon and C # detect surface defects - ROI interaction (III) (functions such as synchronous scaling and clipping with pictures)

ctf-pikachu-RCE

IDEA安装、配置、测试

【Ucos-III源码分析】——软件定时器
![[基础服务] [数据库] MySQL 主从复制部署与配置](/img/6d/0ad81ae080153fc2e50dc60c39e1bd.png)
[基础服务] [数据库] MySQL 主从复制部署与配置

开箱:阿里技术人在读什么书?
![Leetcode refers to the average value of offer II 041 sliding window [sliding window] the way of leetcode in heroding](/img/29/ea4b91cc90ca0dd0eea7e6a3d33394.png)
Leetcode refers to the average value of offer II 041 sliding window [sliding window] the way of leetcode in heroding

Detailed summary of dynamic memory management (C language)

TCP拥塞控制详解 | 6. 主动队列管理

深圳某游戏研发公司给每个工位都装监控,网友:堪比坐牢!
随机推荐
鸿湖万联致远开发板正式合入OpenHarmony主干
Image xmage de Huawei: Cherchez toutes les images du monde et voyez enfin Bodhisattva
PNAs | Nannong zhangruifu group revealed a new signal recognition mechanism of microbial fertilizer functional mycorrhizal chemotaxis
《遥远的救世主》遵守客观规律(六)——文化属性
The web crawler crawls the titles and contents of all chapters of the romance of the Three Kingdoms (beautifulsoup analysis)
移掉K位数字[贪心思想 & 单调栈实现]
标准输入输出流
《遥远的救世主》遵守客观规律(七)——文化属性
C#-线程的加入 进度条加载案例,第一次视频效果的加入啦
【C语言进阶】⑨动态内存分配知识总结 超详细
串的概念相关及模式匹配
C#网络应用编程,实验2:IP地址转换和域名解析练习
【Ucos-III源码分析】——信号量
100% accuracy, Alibaba business travel billing system architecture design practice
module ‘sklearn.datasets‘ has no attribute ‘fetch_mldata‘
It's decided. There are 93 open source tasks in 6 fields. Alibaba open source tutor will take you to participate in the open source summer 2022 of the Chinese Academy of Sciences
PNAS | 南农张瑞福组揭示了微生物肥料功能菌根际趋化的信号识别新机制
c语言指针与数组的深入理解
MySQL超详细安装教程 手把手教你安装MySQL到使用MySQL 最简单的MySQL安装方式,这种方式装,卸载也简单
(note) seven rainbow 30 series graphics card - "one key overclocking" button