浅谈 equals() 和 hashCode()()-其他
浅谈 equals() 和 hashCode()()
和 在 Object 类中定义
和 在 Object 类中定义
equals()
hashCode()
- hashCode():用于计算对象哈希值。
- equals():用于比较对象是否相等。
默认实现
定义的方法
定义的方法
Object
默认实现:均基于对象引用地址。
- equals():比较对象引用地址。
-
hashCode():返回对象引用地址(本地方法)。
public boolean equals(Object obj) {
return (this == obj);
}public native int hashCode();
方法重写
① 原因
为什么要重写?
为什么要重写?
- 哈希集合的检索机制中,使用了 hashCode() 和 equals()。
- 在默认实现下,只有同一个对象引用的 equals() 和 hashCode() 才会相等。
- 在实际业务中,通常认为两个对象属性相同则对象相等。
- 需要成对重写 hashCode() 和 equals(),才能实现对象的区分性。
示例
示例
HashMap<User> userMap = new HashMap<>;
User u1 = new User("secret", 20);
User u2 = new User("secret", 20);
userMap.put(u1, "user1");
userMap.get(u2); // 能获取到吗?
- 未重写的情况:不能。因为 u1 和 u2 是不同的对象引用,hashcode() 和 equals() 比较不相等。
- 重写的情况:能。因为自定义了 hashCode() 计算规则、equals() 比较条件,u1 和 u2 视为相等的对象。
② 重写 equals()
通常认为:对象的属性值完全相同,则对象相等。
通常认为:对象的属性值完全相同,则对象相等。
-
参数检查:
相同引用
判空
判类型:getClass()(严格):返回运行时具体类型,不考虑继承关系。
instanceOf(宽松):测试对象是否为类的实例(或接口实现类)。 - 相同引用
- 判空
- 判类型:
getClass()(严格):返回运行时具体类型,不考虑继承关系。
instanceOf(宽松):测试对象是否为类的实例(或接口实现类)。 - getClass()(严格):返回运行时具体类型,不考虑继承关系。
- instanceOf(宽松):测试对象是否为类的实例(或接口实现类)。
-
比较属性:先向下转型,再比较属性值。
基本类型:使用 ==。
引用类型:使用 Objects.equals() 方法,避免 NPE。
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}// 若严格要求类型一致,条件二改成 this.getClass() != obj.getClass()
if (obj == null || !obj instanceof Person) {
return false;
}Person p = (Person) obj;
// 不使用 xxx.equals(xxx),避免NPE
return Objects.equals(this.name, p.name)
&& this.age == p.age;}
- 基本类型:使用 ==。
-
引用类型:使用 Objects.equals() 方法,避免 NPE。
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}// 若严格要求类型一致,条件二改成 this.getClass() != obj.getClass()
if (obj == null || !obj instanceof Person) {
return false;
}Person p = (Person) obj;
// 不使用 xxx.equals(xxx),避免NPE
return Objects.equals(this.name, p.name)
&& this.age == p.age;}
③ 重写 hashCode()
原则:尽量避免哈希冲突。
此处使用 工具类的方法。
原则:尽量避免哈希冲突。
此处使用 工具类的方法。
Objects
@Override
public int hashCode() {
return Objects.hash(name, age);
}
分析: 的
分析: 的
Objects
hash(...)
-
Objects.hash(…):
可变参数(数组)
调用 Arrays.hashCode(…) 计算哈希值。
public static int hash(Object… values) {
return Arrays.hashCode(values);
} - 可变参数(数组)
-
调用 Arrays.hashCode(…) 计算哈希值。
public static int hash(Object… values) {
return Arrays.hashCode(values);
} -
Arrays.hashCode(…):哈希算法 hash = 31 * hash + hash(i)
基于多个属性计算。
基于迭代的方式计算。
public static int hashCode(Object a[]) {
if (a == null)
return 0;int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());return result;
} - 基于多个属性计算。
-
基于迭代的方式计算。
public static int hashCode(Object a[]) {
if (a == null)
return 0;int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());return result;
}
思考
选用 作为乘子的好处
选用 作为乘子的好处
31
- 质数:在计算时可以尽量减少哈希冲突。
- 性能优化:
代码运行频繁时,JVM 会将 31 * i 优化为位运算 (i<<5) - 1。 位运算的执行效率高。
- 代码运行频繁时,JVM 会将 31 * i 优化为位运算 (i<<5) - 1。
- 位运算的执行效率高。
方法成对重写后, 和 的关系
方法成对重写后, 和 的关系
equals()
hashCode()
结论:
- equals() 和 hashCode() 都相等,才认为对象相同。
-
若 equals() 相等,则 hashCode() 相等。
描述
成立?逆命题
若 hashCode() 相等,则 equals() 相等
(哈希冲突)否命题
若 equals() 不相等,则 hashCode() 不相等
(哈希冲突)逆否命题
若 hashCode 不相等,则 equals() 不相等
描述 | 成立? | |
---|---|---|
逆命题 | 若 hashCode() 相等,则 equals() 相等 |
(哈希冲突) |
否命题 | 若 equals() 不相等,则 hashCode() 不相等 |
(哈希冲突) |
逆否命题 | 若 hashCode 不相等,则 equals() 不相等 |
hashCode()
equals()
equals()
hashCode()
hashCode
equals()
和 在 Object 类中定义
和 在 Object 类中定义
equals()
hashCode()
- hashCode():用于计算对象哈希值。
- equals():用于比较对象是否相等。
默认实现
定义的方法
定义的方法
Object
默认实现:均基于对象引用地址。
- equals():比较对象引用地址。
-
hashCode():返回对象引用地址(本地方法)。
public boolean equals(Object obj) {
return (this == obj);
}public native int hashCode();
方法重写
① 原因
为什么要重写?
为什么要重写?
- 哈希集合的检索机制中,使用了 hashCode() 和 equals()。
- 在默认实现下,只有同一个对象引用的 equals() 和 hashCode() 才会相等。
- 在实际业务中,通常认为两个对象属性相同则对象相等。
- 需要成对重写 hashCode() 和 equals(),才能实现对象的区分性。
示例
示例
HashMap<User> userMap = new HashMap<>;
User u1 = new User("secret", 20);
User u2 = new User("secret", 20);
userMap.put(u1, "user1");
userMap.get(u2); // 能获取到吗?
- 未重写的情况:不能。因为 u1 和 u2 是不同的对象引用,hashcode() 和 equals() 比较不相等。
- 重写的情况:能。因为自定义了 hashCode() 计算规则、equals() 比较条件,u1 和 u2 视为相等的对象。
② 重写 equals()
通常认为:对象的属性值完全相同,则对象相等。
通常认为:对象的属性值完全相同,则对象相等。
-
参数检查:
相同引用
判空
判类型:getClass()(严格):返回运行时具体类型,不考虑继承关系。
instanceOf(宽松):测试对象是否为类的实例(或接口实现类)。 - 相同引用
- 判空
- 判类型:
getClass()(严格):返回运行时具体类型,不考虑继承关系。
instanceOf(宽松):测试对象是否为类的实例(或接口实现类)。 - getClass()(严格):返回运行时具体类型,不考虑继承关系。
- instanceOf(宽松):测试对象是否为类的实例(或接口实现类)。
-
比较属性:先向下转型,再比较属性值。
基本类型:使用 ==。
引用类型:使用 Objects.equals() 方法,避免 NPE。
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}// 若严格要求类型一致,条件二改成 this.getClass() != obj.getClass()
if (obj == null || !obj instanceof Person) {
return false;
}Person p = (Person) obj;
// 不使用 xxx.equals(xxx),避免NPE
return Objects.equals(this.name, p.name)
&& this.age == p.age;}
- 基本类型:使用 ==。
-
引用类型:使用 Objects.equals() 方法,避免 NPE。
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}// 若严格要求类型一致,条件二改成 this.getClass() != obj.getClass()
if (obj == null || !obj instanceof Person) {
return false;
}Person p = (Person) obj;
// 不使用 xxx.equals(xxx),避免NPE
return Objects.equals(this.name, p.name)
&& this.age == p.age;}
③ 重写 hashCode()
原则:尽量避免哈希冲突。
此处使用 工具类的方法。
原则:尽量避免哈希冲突。
此处使用 工具类的方法。
Objects
@Override
public int hashCode() {
return Objects.hash(name, age);
}
分析: 的
分析: 的
Objects
hash(...)
-
Objects.hash(…):
可变参数(数组)
调用 Arrays.hashCode(…) 计算哈希值。
public static int hash(Object… values) {
return Arrays.hashCode(values);
} - 可变参数(数组)
-
调用 Arrays.hashCode(…) 计算哈希值。
public static int hash(Object… values) {
return Arrays.hashCode(values);
} -
Arrays.hashCode(…):哈希算法 hash = 31 * hash + hash(i)
基于多个属性计算。
基于迭代的方式计算。
public static int hashCode(Object a[]) {
if (a == null)
return 0;int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());return result;
} - 基于多个属性计算。
-
基于迭代的方式计算。
public static int hashCode(Object a[]) {
if (a == null)
return 0;int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());return result;
}
思考
选用 作为乘子的好处
选用 作为乘子的好处
31
- 质数:在计算时可以尽量减少哈希冲突。
- 性能优化:
代码运行频繁时,JVM 会将 31 * i 优化为位运算 (i<<5) - 1。 位运算的执行效率高。
- 代码运行频繁时,JVM 会将 31 * i 优化为位运算 (i<<5) - 1。
- 位运算的执行效率高。
方法成对重写后, 和 的关系
方法成对重写后, 和 的关系
equals()
hashCode()
结论:
- equals() 和 hashCode() 都相等,才认为对象相同。
-
若 equals() 相等,则 hashCode() 相等。
描述
成立?逆命题
若 hashCode() 相等,则 equals() 相等
(哈希冲突)否命题
若 equals() 不相等,则 hashCode() 不相等
(哈希冲突)逆否命题
若 hashCode 不相等,则 equals() 不相等
描述 | 成立? | |
---|---|---|
逆命题 | 若 hashCode() 相等,则 equals() 相等 |
(哈希冲突) |
否命题 | 若 equals() 不相等,则 hashCode() 不相等 |
(哈希冲突) |
逆否命题 | 若 hashCode 不相等,则 equals() 不相等 |
hashCode()
equals()
equals()
hashCode()
hashCode
equals()