参考:https://blog.youkuaiyun.com/en_joker/article/details/133670740
https://www.mianshiya.com/bank/1787463103423897602/question/1772568783219695618#heading-1
首先分清楚:
hashCode
、equals
和==
都是Java中用于比较对象的三种方式,但是它们的用途和实现还是有很大区别的。
-
hashCode
用于散列存储结构中确定对象的存储位置,可用于快速比较两个对象是否不同、因为如果它们的哈希码不同、那么它们肯定不相等。 -
equals
用于比较两个对象的内容是否相等,通常需要重写自定义比较逻辑。 -
==
用于比较两个引用是否指向同一个对象(即内存地址)。对于基本数据类型,比较它们的值。
其中hashCode默认由Object类根据对象的内存地址生成哈希码。
所以,需要注意的是哈希表进行==
或!=
比较时,
如果重写了hashCode()
方法的话,hashCode
一样的,==
不一定判断相等
如果没有重写的话,hashCode
一样的,==
能判断相等
(1)需要重写hashCode方法的原因:
因为在Java中,哈希表使用哈希码来确定存储对象的位置。如果两个相等的对象具有不同的哈希码,那么它们将被存储在哈希表的不同位置(不在同一个哈希桶中),导致无法正确查找这些对象。(比如无法正常使用contains,get方法等等)
例子如下:
package com.wb;
import java.util.*;
public class IncorrectKey {
private final String value;
private final int randomHash; // 用于模拟错误的哈希码生成
private final int rightHash; // 用于模拟正确的哈希码生成
public IncorrectKey(String value) {
this.value = value;
this.randomHash = (int) (Math.random() * 1000); // 错误:哈希码基于随机值
this.rightHash = Objects.hash(value);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
IncorrectKey that = (IncorrectKey) o;
return value.equals(that.value); // 正确:仅比较 value 是否相等
}
@Override
public int hashCode() {
//return randomHash; // 错误:哈希码与 equals 比较的 value 无关
return rightHash;// 正确:重写哈希码,两个对象不一定是同一个地址,但是会被放到同一个哈希桶中
}
public static void main(String[] args) {
System.out.println("--------------例子1,HashMap-----------------");
Map<IncorrectKey, String> map = new HashMap<>();
IncorrectKey key1 = new IncorrectKey("key");
IncorrectKey key2 = new IncorrectKey("key"); // key1.equals(key2) 为 true
map.put(key1, "Value1");
//虽然equals相等,但是Hash中,必须使hashCode相等
System.out.println(key1.equals(key2));// 一直都是true
// 尝试用 key2 获取值
String result = map.get(key2);
System.out.println("查找到的值: " + result); // rightHash对应"Value1",randomHash对应null
System.out.println("--------------例子2,HashSet-----------------");
// HashSet
Set<IncorrectKey> set = new HashSet<>();
set.add(key1);
System.out.println(set.contains(key2));//rightHash对应true,randomHash对应false
}
}
(2)需要重写equals
方法的原因:
contains原理如下:
所以才需要重写equals
方法。(但是实际上涉及到Hash相关的数据结构,还要同时重写hashCode
方法!)
(3)两者需一起用,例子如下:
package com.wb;
import java.util.Objects;
public class Students {
String name;
int age;
public Students(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Students students = (Students) o;
return name.equals(students.name) && age == students.age;
//return Objects.equals(name,students.name) && age == students.age;//等效于上面,两种相同效果的equals调用方法。
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
public static void main(String[] args) {
// 假如这样重写hashCode和equals
Students s1 = new Students("20", 24);
Students s2 = new Students("20", 24);
Students s3 = s1;
// 默认是引用指向的对象的内存地址得出的hashCode
System.out.println(s1.hashCode() + "==" + s3.hashCode());// 打印50523==50523
System.out.println(s1.equals(s2)); //打印true
System.out.println(s1.hashCode() + "==" + s2.hashCode());// 打印50523==50523
System.out.println(s1 == s2);//打印false
//由此看出,如果重写了hashCode()方法的话,hashCode一样的,==不一定判断相等
//如果没有重写的话,hashCode一样的,==能判断相等
}
}