java.lang.Object中equals()和hashCode()
- equals()
- hashCode()
- “==”比较的是什么
- 为什么覆写equals()的同时需要覆写hashCode
一、equals()
- 自反性
对于任意非null的引用值x,x.equals(x)必须返回true - 对称性
对于任意的非null的引用值x、y,如果x.equals(y)返回true,那么y.equals(x)也必须返回true - 传递性
对于任意的非null的引用值x、y、z如果x.equals(y)返回true,同时y.equals(x)返回true;那么x.equals(z)也要返回true - 一致性
对于任意的非null的引用值x、y,如果equals中需要比较的属性没有改变,那么多次调用x.equals(y)返回值是一样的 对于任意的非null引用值,x.equals(null)肯定会返回false
equals()覆写方法
package java_lang_Object;
import java.util.HashMap;
/**
* Created by luckyboy on 2018/7/1.
*/
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;
}
@Override
public boolean equals(Object obj) {
/*1、将传进来的参数Object使用==进行比较,如果参数引用值和对象的隐式参数this引用值的引用地址一样,
* 那么返回true,否则返回false
*/
if(this == obj){
return true;
}
/**
*使用instanceof判断传递进来的参数是否是该类对象,如果不是则返回false,如果是则进行下一步
*/
if(!(obj instanceof Person)){
return false;
}
/**
*将方法参数转换成该类的类型
*/
Person p = (Person) obj;
/**
*对于类中每个需要检查的域,我们都需要判断参数的域是否和该对象中的对应的域相匹配
*如果域是基本类型,那么我们直接使用"=="进行判断
*如果域是引用类型,我们可以调用引用类型的equals方法进行判断
*对于引用类型的检查,建议使用下面的方式检查是否匹配
*(this.field == o.field) ||((field != null) && field.equals(o.field))
*/
if(this.name.equals(p.name) &&
this.age == p.age &&
this.address == p.address){
return true;
}
return false;
}
public static void main(String[] args){
Person p1 = new Person("Zou",22,"Hunan");
Person p2 = new Person("Zou",22,"Hunan");
System.out.println("p1.hashCode = " + p1.hashCode());
System.out.println("p2.hashCode = " + p2.hashCode());
System.out.println("p1.equals(p2) = "+ p1.equals(p2));
HashMap<Person,String> map = new HashMap<Person,String>();
map.put(p1,"Zou");
String s = map.get(p2);
System.out.print(s);
}
}
二、hashCode()
Object方法中的hashCode()
Object中的hashCode() 返回对象的32位jvm内存地址;如果一个类没有重写hashCode方法,那么对于任意的两个不同的对象,其地址都是不相同的,那么hashCode()返回值是不同的
public native int hashCode();
看下面的例子
@Test public void testString(){ String s1 = "hello"; String s2 = "hello"; String s3 = "hel"+"lo"; String s4 = "hel"+new String("lo"); String s5 = new String("hello"); System.out.println("s1 == s2 "+(s1 == s2)); System.out.println("s1 == s3 "+(s1 == s3)); System.out.println("s1 == s4 "+(s1 == s4)); System.out.println("s1 == s5 "+(s1 == s5)); System.out.println("s1.hashCode == "+s1.hashCode()); System.out.println("s2.hashCode == "+s2.hashCode()); System.out.println("s3.hashCode == "+s3.hashCode()); System.out.println("s4.hashCode == "+s4.hashCode()); System.out.println("s5.hashCode == "+s5.hashCode()); System.out.println("s1.equals(s2)" + (s1.equals(s2))); System.out.println("s1.equals(s3)" + (s1.equals(s3))); System.out.println("s1.equals(s4)" + (s1.equals(s4))); System.out.println("s1.equals(s5)" + (s1.equals(s5))); }
输出结果
s1 == s2 true s1 == s3 true s1 == s4 false s1 == s5 false s1.hashCode == 99162322 s2.hashCode == 99162322 s3.hashCode == 99162322 s4.hashCode == 99162322 s5.hashCode == 99162322 s1.equals(s2)true s1.equals(s3)true s1.equals(s4)true s1.equals(s5)true
从上面的结果可以知道,如果一个方法重写了hashCode()方法,那么返回就不再是内存地址
hashCode方法归约
1、如果两个对象的equals()返回true,那么两个对象的hashCode()返回一致
2、如果两个hashCode()返回一致,两个对象执行equals()不一定相等
3、如果一个对象的equals()要比较的属性不发生变化,那么对同一个对象调用多次,那么该对象返回的hashCode方法一致hashCode()重写规则
1、把某个非零的的常数值保存在一个名为result的int类型变量中
2、对于对象中每一个关键属性f(equals 中设计需要比较的属性)完成以下步骤a、如果该域是boolean类型,则计算f ? 1:0 b、如果该域是byte、char、short、int类型,则计算int(f); c、如果该域是long类型,则计算int(f ^ (f>>32)) d、如果该域是float类型,则计算Float.floatToIntBits(f) e、如果该域是double类型,则计算Double.doubleToLongBits(f)得到long类型的数据,然后根据c得到一个int型数值 f、如果该域是一个引用类型,则计算f == null ?f.hashCode():0 g、如果该域是一个数组类型,那么需要对数组中每一个元素都进行上面的规则中的计算方法
三、”==”比较的是什么
直接使用==操作符,比较的是两个变量的引用的实例变量的地址
四、为什么覆写equals()的同时需要覆写hashCode
我们可以来看一个Person对象作为key添加到HashMap中的例子
package java_lang_Object;
import java.util.HashMap;
/**
* Created by luckyboy on 2018/7/1.
*/
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;
}
@Override
public boolean equals(Object obj) {
if(this == obj){
return true;
}
if(!(obj instanceof Person)){
return false;
}
Person p = (Person) obj;
if(this.name.equals(p.name) &&
this.age == p.age &&
this.address == p.address){
return true;
}
return false;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
public static void main(String[] args){
Person p1 = new Person("Zou",22,"Hunan");
Person p2 = new Person("Zou",22,"Hunan");
System.out.println("p1.hashCode = " + p1.hashCode());
System.out.println("p2.hashCode = " + p2.hashCode());
System.out.println("p1.equals(p2) = "+ p1.equals(p2));
HashMap<Person,String> map = new HashMap<Person,String>();
map.put(p1,"Zou");
String s = map.get(p2);
System.out.print(s);
}
}
```
输出
p1.hashCode = 1836019240
p2.hashCode = 325040804
p1.equals(p2) = true
null
重写hashCode方法之后
package java_lang_Object;
import java.util.HashMap;
/**
* Created by luckyboy on 2018/7/1.
*/
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;
}
@Override
public boolean equals(Object obj) {
if(this == obj){
return true;
}
if(!(obj instanceof Person)){
return false;
}
Person p = (Person) obj;
if(this.name.equals(p.name) &&
this.age == p.age &&
this.address == p.address){
return true;
}
return false;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
result = 31 * result + (address != null ? address.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
public static void main(String[] args){
Person p1 = new Person("Zou",22,"Hunan");
Person p2 = new Person("Zou",22,"Hunan");
System.out.println("p1.hashCode = " + p1.hashCode());
System.out.println("p2.hashCode = " + p2.hashCode());
System.out.println("p1.equals(p2) = "+ p1.equals(p2));
HashMap<Person,String> map = new HashMap<Person,String>();
map.put(p1,"Zou");
String s = map.get(p2);
System.out.print(s);
}
}
```
输出
```
p1.hashCode = 156624696
p2.hashCode = 156624696
p1.equals(p2) = true
Zou
```
其中主要是HashMap的添加过程是首先将要添加的key进行一次hash操作,然后就映射到了一个桶中;一个对象如果没有重写hashCode(),那么两个
Person p1 = new Person("Zou",22,"Hunan");
Person p2 = new Person("Zou",22,"Hunan");
对象的hashCode()返回值是对应内存的地址,肯定是不相同的。那么这个时候我们再次去查找就找不到我们的信息。相比较Person类重写了hashCode(),那么p1和p2的hashCode()返回值是一样的,这是会将p2映射到p1所在的桶中然后根据equals方法去判断是否相等,相等则返回对应的value
参考文章
http://www.importnew.com/25783.html
http://baijiahao.baidu.com/s?id=1561228085482413&wfr=spider&for=pc
《effective java》