一、重写了equals为什么还要重写hashcode
一、为什么重写了equals要重写hashcode?
有这么一个场景,当用户登录时,来了两个user,有name和age,并且还有手机号,手机号相同的我认为是一个用户。那么我们很容易得到以下代码:
import java.util.HashMap; public class MockLogin { static class User { public int age; public String name; public String mobile; public User( String mobile,int age, String name) { this.age = age; this.name = name; this.mobile = mobile; } } public static void main(String[] args) { //小灰灰登录 User xhhFirstLogin = new User("123456",21, "xiaohuihui"); //这里我想记录小灰灰是否登陆过 HashMap<User,Boolean> map = new HashMap<>(); map.put(xhhFirstLogin,true); //小灰灰第二次登陆来了,如果登陆过,我就把之前登陆信息给他,不再次登陆 User xhhSecondLogin = new User("123456",21, "xiaohuihui"); boolean b = map.containsKey(xhhSecondLogin); //看是否登陆过 System.out.println(b?"登陆过":"没有登录"); } }
但实际运行结果是:
没有登录
聪明的你一定看出来了,你没有重写equals啊,怎么判断手机号相等是同一个对象,好,那我们重写一下equals:
public class MockLogin { static class User { public int age; public String name; public String mobile; public User( String mobile,int age, String name) { this.age = age; this.name = name; this.mobile = mobile; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return Objects.equals(mobile, user.mobile); } } public static void main(String[] args) { //小灰灰登录 User xhhFirstLogin = new User("123456",21, "xiaohuihui"); //这里我想记录小灰灰是否登陆过 HashMap<User,Boolean> map = new HashMap<>(); map.put(xhhFirstLogin,true); //小灰灰第二次登陆来了,如果登陆过,我就把之前登陆信息给他,不再次登陆 User xhhSecondLogin = new User("123456",21, "xiaohuihui"); boolean b = map.containsKey(xhhSecondLogin); //看是否登陆过 System.out.println(b?"登陆过":"没有登录"); } }
重写完之后的结果:
没有登录
还是没有登录。那么问题究竟出在哪里了呢?既然是containsKey返回的false,我们就去看看containsKey是怎么写的吧
public boolean containsKey(Object key) { return getNode(key) != null; }
接着往下找:
/** * Implements Map.get and related methods. * * @param key the key * @return the node, or null if none */ final Node<K,V> getNode(Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n, hash; K k; if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & (hash = hash(key))]) != null) { if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first; if ((e = first.next) != null) { if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }
哦吼,这么多代码,关掉你的文章,休息一下。秋冬马爹,其实这里面只有一个行我们是需要注意的:
hash = hash(key));//根据key算出来的hash if (first.hash == hash && //对比第一个节点的hash值 ((k = first.key) == key || (key != null && key.equals(k)))) //如果key和第一个节点的key相同或者(key不等于空并且相等) return first;
我们知道hashmap是一个拉链式的hash结构:
containsKey是先找到hashcode,然后再来对比是否相等的。那么我们很容易得到猜想,是不是刚才hashcode不一样,导致的containsKey返回了false。我们先来打印一下hashcode:
.... boolean b = map.containsKey(xhhSecondLogin); System.out.println("fcode = "+xhhFirstLogin.hashCode()+" scode = "+xhhSecondLogin.hashCode());
看一下执行结果:
fcode = 1704856573 scode = 705927765 没有登录
果然,是hashcode导致了我们containsKey函数的失败。那我们先简单的处理一下,让他们相等:
public User( String mobile,int age, String name) { this.age = age; this.name = name; this.mobile = mobile; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return Objects.equals(mobile, user.mobile); } @Override public int hashCode() { return 1; } }
在user类增加hashcode方法,返回固定值1,我们再跑一下运行结果:
fcode = 1 scode = 1 登陆过
这时真的相等了,当然hashcode = 1这是为了做测试,工程化落地时,我们还是用hashcode标准方法吧:
@Override public int hashCode() { return Objects.hash(age, name, mobile); }
我们认为,当他所有成员属性一样时,那就是一个对象,再次跑结果:
fcode = -679329960 scode = -679329960 登陆过
技术总结:
-
hashcode是一种快速定位到拉链法hash表数组位置索引的一个值,根据key算出来。
-
hashcode重写主要是为了解决对象在hash表中当key时,equals相等,但是hashcode不相等,导致containsKey错误返回的问题
贴一下最后的源代码:
package com.android; import java.util.HashMap; import java.util.Objects; public class MockLogin { static class User { public int age; public String name; public String mobile; public User( String mobile,int age, String name) { this.age = age; this.name = name; this.mobile = mobile; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return Objects.equals(mobile, user.mobile); } @Override public int hashCode() { return Objects.hash(age, name, mobile); } } public static void main(String[] args) { //小灰灰登录 User xhhFirstLogin = new User("123456",21, "xiaohuihui"); //这里我想记录小灰灰是否登陆过 HashMap<User,Boolean> map = new HashMap<>(); map.put(xhhFirstLogin,true); //小灰灰第二次登陆来了,如果登陆过,我就把之前登陆信息给他,不再次登陆 User xhhSecondLogin = new User("123456",21, "xiaohuihui"); boolean b = map.containsKey(xhhSecondLogin); System.out.println("fcode = "+xhhFirstLogin.hashCode()+" scode = "+xhhSecondLogin.hashCode()); //看是否登陆过 System.out.println(b?"登陆过":"没有登录"); } }
热门文章
- 济南宠物领养活动有哪些(济南宠物领养活动有哪些地方)
- node.js – 包、express
- Python数据类型%s,%d,%f的用法
- 南京宠物狗领养地址在哪里呀(南京宠物狗领养地址在哪里呀最近)
- 「12月27日」最高速度23M/S,2024年Hiddify Next每天更新免费节点订阅链接
- 长沙宠物猫领养中心在哪里 长沙宠物猫领养中心在哪里啊
- vue3.x+vite+element-ui+vue-router+vuex+axios搭建项目
- 「12月11日」最高速度18.3M/S,2024年Hiddify Next每天更新免费节点订阅链接
- 「11月24日」最高速度21.6M/S,2024年Hiddify Next每天更新免费节点订阅链接
- 上海领养宠物狗的平台电话(上海领养狗狗机构)