为什么重写equals一定要重写hashCode方法?
大家都知道,equals和hashcode是java.lang.Object类的两个重要的方法,在实际应用中常常需要重写这两个方法,但至于为什么重写这两个方法很多人都搞不明白。
下面我们看下Object类中默认的equals和hashCode方法的实现:
1 public boolean equals(Object obj) { 2 return (this == obj); 3 } 4 5 public native int hashCode();
以上是Object类关于这两个方法的源码,Object类默认的equals比较规则就是比较两个对象的内存地址。而hashcode是本地方法,但实际上,hashcode是根据对象的内存地址经哈希算法得来的。
1 import lombok.AllArgsConstructor; 2 import lombok.Data; 3 4 /** 5 * Created by ganbo on 2019/6/17. 6 */ 7 8 @Data 9 @AllArgsConstructor 10 public class User { 11 private Long id; 12 private String name; 13 14 @Override 15 public boolean equals(final Object o) { 16 if (this == o) return true; 17 if (!(o instanceof User)) return false; 18 if (super.equals(o)) return true; 19 20 final User user = (User) o; 21 22 if (id != null ? !id.equals(user.id) : user.id != null) return false; 23 return name != null ? name.equals(user.name) : user.name == null; 24 } 25 26 @Override 27 public int hashCode() { 28 int result = 1; 29 result = 31 * result + (id != null ? id.hashCode() : 0); 30 result = 31 * result + (name != null ? name.hashCode() : 0); 31 return result; 32 } 33 }
上面代码展示了重写equals和hashCode方法后的User类,线有如下代码:
1 public static void main(String[] args) { 2 User u1 = new User(1L, "root"); 3 User u2 = new User(1L, "root"); 4 5 System.out.println(u1.equals(u2)); 6 System.out.println(u1.hashCode() == u2.hashCode()); 7 }
此时u1.equals(u2)一定返回true,假如只重写equals而不重写hashCode,那么User类的hashCode方法默认就是继承父类Object的hashCode方法,由于默认的hashCode方法是根据对象的内存地址
经过hash算法得来的,显然此时u1对象和u2对象的hashCode值不一定相等,这个时候将该对象作为HashMap的key就会出现问题,跟期望的不一致(两个对象equals,却存在MashMap的两个槽上)。
然而重写了equals,且u1.equals(u2)返回true,根据hashcode的规则,两个对象相等其哈希值一定相等,所以矛盾就产生了,因此重写equals一定要重写hashcode,而且从类重写后的hashcode方法中可以看出,
重写后返回的新的哈希值与User的两个属性有关。
以下是关于hashcode的一些规定:
两个对象相等,hashcode一定相等
两个对象不等,hashcode不一定不等
hashcode相等,两个对象不一定相等
hashcode不等,两个对象一定不等
原文地址:https://www.cnblogs.com/ganbo/p/11039053.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 一天一大 leet(最佳观光组合)难度:中等 DAY-17
- Django环境搭建
- javascript 中的位运算符
- python 操作 redis 的一些例子
- mongoDB(一)生产环境基础实践
- 一天一大 leet(验证回文串)难度:简单 DAY-19
- mongoDB(二)mongoDB副本集实战
- 一天一大 leet(正则表达式匹配)难度:困难 DAY-20
- mongoDB (三) mongoDB分片集群
- mongoDB (四) mongoDB认证
- 一天一大 leet(二叉树中的最大路径和)难度:困难 DAY-21
- 一天一大 leet(把数字翻译成字符串)难度:中等 DAY-9
- 一天一大 leet(模式匹配)难度:中等 DAY-22
- 一天一大 leet(二进制求和)难度:简单 DAY-23
- xmake v2.3.7 发布, 新增 tinyc 和 emscripten 工具链支持