本文主要介绍了hashCode()与equals()的原理,并在讲述了重写二者的方法体,以及其在集合中的使用(保证集合中对象内容不重复!)
文章目录
前言
在java中有一个共同的父类 Object ,所有对象均隐性继承了此类,其中 equals() 与 hashCode() 就是继承过来的方法。
equals() 默认在比较两个对象时,比较的是其地址(因为所有类继承Object,Object其内部使用的是==比较,==用于判断两边的内存地址是否相同)
eg:person01.equals(person02)是在判断两个对象在内存堆中的地址是否相同。由于是继承父类的方法,我们在编写程序时自然可以重写此方法,我们熟知的String在其内部就已经重写了equals方法(自己定义的类默认未重写),如下所示!
hashCode() 与equals()相似,但其是用native修饰的方法(下面会有解释),它默认返回的是一个整数(jvm默认有一些契约,各厂商默认都会遵循此契约,下面会有解释),但自定义的类都默认继承Object,自然也可以重写hashCode()方法(在TreeSet中存储对象会使用此方法,下面会有详解!)
Object中equals()与hashCode()方法
- 一个native方法就是一个Java调用非Java代码的接口。一个native方法是指该方法的实现由非Java语言实现,比如用C或C++实现。
自定义类中equals()与hashCode()方法(未重写)
String内部重写的equals方法
1.equals()
(1)equals()简介
equals()在不重写时,默认继承Object的方法,在Object的equals()使用 == 判断,==判断的是两个对象的地址是否相同,String是个例外其在内部重写了equals方法,所以可以使用equals()方法判断字符串内容是否相等,在我们自定义的类中可以重写equals方法,实现对自定义对象内容的判断!
(2)equals()重写(实现对自定义类对象内容是否相等的判断)
步骤:
- 判断此对象与传过来对象的地址是否相同
- 使用instanceof判断类型
- 根据类型结果(可能是,也可能不是),进行类型转换,比较各属性值
代码示例如下:
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
//判断此对象与传过来对象的地址是否相同
if (this==obj) {
return true;
//判断obj是否属于Person类
}else if (obj instanceof Person) {
//如果是,父类转子类
Person p=(Person)obj;
//判断各个属性是否相同
if (p.name.equals(this.name)&&p.age==this.age) {
return true;
}
}
//不相同返回false
return false;
}
2.hashCode()与内存地址
结论: hashCode返回的并不一定是对象的(虚拟)内存地址,只是可能会与内存地址有关,具体取决于运行时库和JVM的具体实现。
(1)native
native主要用于方法上
- 一个native方法就是一个Java调用非Java代码的接口。一个native方法是指该方法的实现由非Java语言实现,比如用C或C++实现。
- 在定义一个native方法时,并不提供实现体(比较像定义一个Java Interface),因为其实现体是由非Java语言在外面实现的
- 注意:
主要是因为JAVA无法对操作系统底层进行操作,但是可以通过jni(java native interface)调用其他语言来实现底层的访问。
(2)Object.hashCode()
参考:Java的Object.hashCode()的返回值到底是不是对象内存地址?
Object.hashCode是一个native方法,看不到源码(Java代码,Oracle的JDK是看不到的,OpenJDK或其他开源JRE是可以找到对应的C/C++代码)。
Object.hashCode()在JRE(Java Runtime Library)中应该遵循的一些契约(contract):(PS:所谓契约当然是大家一致达成的,各个JVM厂商都会遵循)
- 一致性(consistent),在程序的一次执行过程中,对同一个对象必须一致地返回同一个整数。
- 如果两个对象通过equals(Object)比较,结果相等,那么对这两个对象分别调用hashCode方法应该产生相同的整数结果。(PS:这里equals和hashCode说的都是Object类的)
- 如果两个对象通过java.lang.Object.equals(java.lang.Ojbect)比较,结果不相等,不必保证对这两个对象分别调用hashCode也返回两个不相同的整数。
(3)特殊hashCode
- String:只要字符串内容相同,hashCode也相同。
- Integer:返回的hashCode是其对象内包含的整数的值。
(4)重写Object.hashCode()
@Override
public int hashCode() {
// TODO Auto-generated method stub
//返回属性的hashCode,不使用jvm默认契约返回的hashCode值
//字符串只要值相等,其hashCode值就相等
int numName=this.name.hashCode();
return numName+this.age;
}
3.hashCode()与equals()在集合中的使用
- 如果两个对象equals()为true,那hashCode一定相同,反过来不成立,即如果两个对象hashCode相同,不能得出这两个对象相等。
- 如果用对象当做Map的key,或在Set集合中存储数据时(
若要保证值的唯一性
),那么就要同时重写hashCode和equals()才行,而且要保证两者的一致性。
原因:
(在Set集合添加数据时,会先判断新添加的数据与集合内原有的数据的调用hashCode()方法判断hashCode值是否相等,若不相等直接将新元素添加进集合内,若相等再调用equals()方法判断其结果是否为true,若为true则不添加,false则添加)
- 代码示例:
测试结果:
Test.java
public class Test {
public static void main(String[] args) {
Person person01=new Person("张三", 18);
Person person02=new Person("张三", 18);
Person person03=new Person("李四", 18);
HashSet<Person> hashSet=new HashSet<Person>();
hashSet.add(person01);
hashSet.add(person02);
hashSet.add(person03);
Iterator<Person> iterator=hashSet.iterator();
while(iterator.hasNext()) {
Person person=iterator.next();
System.out.println(person);
}
}
}
Person.java
package day15.demo01;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Person() {
super();
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
//判断此对象与传过来对象的地址是否相同
if (this==obj) {
return true;
//判断obj是否属于Person类
}else if (obj instanceof Person) {
//如果是,父类转子类
Person p=(Person)obj;
//判断各个属性是否相同
if (p.name.equals(this.name)&&p.age==this.age) {
return true;
}
}
//不相同返回false
return false;
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
//返回属性的hashCode,不使用jvm默认契约返回的hashCode值
//字符串只要值相等,其hashCode值就相等
int numName=this.name.hashCode();
return numName+this.age;
}
}