Java 为什么重写equals的时候必须重写hashCode
Posted 赵晓东-Nastu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 为什么重写equals的时候必须重写hashCode相关的知识,希望对你有一定的参考价值。
一、对于包装类型的比较,使用的是equals方法 而不是==
1、首先equals是Object中的方法,Object中equals方法是怎么实现的呢。
public boolean equals(Object obj)
return (this == obj);
2、但是对于对象来说,==比较的是对象在内存中的地址,而不是值的大小,为了能比较对应的值的大小,包装类重写了
Object中的equals方法,使之比较对应的值的大小。
案例:
public class BackTrace
public static void main(String[] args)
Object obj = new Object();
obj.equals(obj);
new BackTrace().f();
public void f()
Integer i1 = new Integer(1);
Integer i2 = new Integer(1);
Double d = new Double(1.00);
if (i1 == i2)
System.out.println("A");
if (i1.equals(i2))
System.out.println("B");
if (i1.equals(d))
System.out.println("C");
我们可以看一下打印的结果是什么
为什么没有打印C呢?我们来看一下源码
public boolean equals(Object obj)
if (obj instanceof Integer)
return value == ((Integer)obj).intValue();
return false;
在源码中,我们会首先进行判断传进来的Obj是否属于Integer,如果不属于Integer则直接返回false,所以我们会直接返回false而不会输出C。
再接下来,我们看一下输出B,源码中给出的是intValue() 而不是比较的地址,所以会输出B
二、对自定义类型如何比较
1、肯定不能用==符号来比较
2、重写equals方法,自定义比较规则,如果一个人的姓名和年龄是相同的,那么就认为同一个人
3、如果重写equals方法,则必须重写hashCode方法(为什么?)
/**
* @Classname Person
* @Description 人
* @Date 2022/1/4 14:09
* @Author zhaoxiaodong
*/
public class Person
//姓名、年龄、地址
private String name;
private int age;
private String address;
public Person(String name, int age, String address)
this.name = name;
this.age = age;
this.address = address;
public String getName()
return name;
public void setName(String name)
this.name = name;
public int getAge()
return age;
public void setAge(int age)
this.age = age;
public String getAddress()
return address;
public void setAddress(String address)
this.address = address;
public void f2()
Person p1 = new Person("张三",10,"北京");
Person p2 = new Person("张三",10,"北京");
if (p1.equals(p2))
System.out.println("Hello World");
public static void main(String[] args)
new BackTrace().f2();
在这个方法中,我们并没有重写Person中的equals和hashCode,调用这个方法,发现并没有输入Hello World这句话。
我们现在重写equals和hashCode
@Override
public boolean equals(Object o)
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name) &&
Objects.equals(address, person.address);
@Override
public int hashCode()
return Objects.hash(name, age, address);
结果,我们就发现输出了。
总结:
1、equals 是 Object 中的方法,实现方式还是==
2、各个包装类都重写了equals方法,来实现自己的比较逻辑,String类也重写了equals方法。
3、自定义的类比较也要重写equals方法
4、重写equals方法必须重写hashCode;
三、为什么重写equals方法必须重写hashCode;
1、hashCode也是Object中的方法,看源码如何实现的。
2、native关键字说明这个方法是原生函数,也就是这个方法是用c/c++语言实现的,并且被编译成了DLL,
由Java去调用,这些函数的实现体在DLL中,JDK源代码中并不包含,你应该是看不到的。
一般情况下,对象不同,hashCode也就不同
下面的是特殊情况:
System.out.println("Aa".hashCode());
System.out.println("BB".hashCode());
2112
2112
已经重写了equals,当姓名和年龄相同的时候,就认为他们是一个人。
这里的p1和p2就是同一个人,那么我们用Map去存储他的分数(没有重写hashCode),正常情况下,我们期望返回95。
我们现在将Person中的hashCode去掉,然后用HashMap去存储和获取。
public static void main(String[] args)
//Person为键,String为值
HashMap<Person,String> map = new HashMap<>();
map.put(new Person("张三",10,"北京"),"95");
String s = map.get(new Person("张三", 10, "北京"));
System.out.println(s);
会发现返回的是Null
所以并没有通过张三这个对象拿到对应的分数,如果将这个hashCode解开会发现拿到了这个分数。
HashMap的存储方式
public V put(K key, V value)
return putVal(hash(key), key, value, false, true);
static final int hash(Object key)
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
HashMap的存储方式,使用key的hash值 作为Key来存储对应的值。
如果我们没有重写hashCode的时候,我们会用Object自带的hashCode去存储,如果重写了hashCode,我们会以年龄和姓名进行获取返回相同的hash值。
3、HashMap哈HashSet的存取都是这样实现的。
4、发生了hash碰撞怎么办,调用equals方法
5、HashMap的存储结构时怎么样的?
对于equals和hashCode,Object规范:
1、在应用程序执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对同一个对象的多次调用,hashCode方法都必须始终返回同一个值。
(对于Person中的姓名和年龄等属性相同,那么hashCode返回同一个值)
2、如果两个对象根据equals方法比较是相等的,那么调用这两个对象的hashCode方法都必须产生同样的整数结果
3、如果两个对象根据equals方法比较是不相等的,那么调用这两个对象中的hashCode的方法则不一定要求hashCode方法必须产生不同的结果。
但是开发人员应该知道,给不相等的对象产生不同的整数结果,有可能提高散列表的性能。
总结:
1、HashCode也是Object中的方法
2、Native关键字说明这个方法是原生函数,也就是这个方法是用c/c++语言实现的,我们看不到源码的实现。
3、一般情况下,对象不同(equals不同),hashCode一般不同,但是对象相同(equals相同),hashCode一定相同。
4、HashMap和HashSet的存取都是通过key的hash值来存储的
以上是关于Java 为什么重写equals的时候必须重写hashCode的主要内容,如果未能解决你的问题,请参考以下文章