复习equals、hashCode的知识点
Object 内部equals实现
public boolean equals(Object obj) {
return (this == obj);
}
上述的代码表明,如果是调用Object的equals,实际上是比较两个对象的内存地址是否一致。
equals和==区别
这个问题在面试中是经常被问,我们都知道equals是比较两个对象的值,而==是比较的两个对象的内存地址。但是从严格意义上说是有问题的。
Car c= new Car(12);
Car c1 =new Car(23);
System.out.println(c.equals(c1));//false
System.out.println(c==c1);//false
这里你在调用equals实际上是调用的是Object的equals(还是比较的是内存地址)。你可以通过重写equals方法。
重写equals的规则
- 自反性。对于任何非null的引用值x,x.equals(x)应返回true。
- 对称性。对于任何非null的引用值x与y,当且仅当:y.equals(x)返回true时,x.equals(y)才返回true。
- 传递性。对于任何非null的引用值x、y与z,如果y.equals(x)返回true,y.equals(z)返回true,那么x.equals(z)也应返回true。
- 一致性。对于任何非null的引用值x与y,假设对象上equals比较中的信息没有被修改,则多次调用x.equals(y)始终返回true或者始终返回false。
- 对于任何非空引用值x,x.equals(null)应返回false。(不要使用null.equals(x) 会出现空指针异常)
equals混合了继承
public class Car {
private int size;
public Car(int size) {
super();
this.size = size;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if(obj instanceof Car) {
Car c = (Car)obj;
return c.size==size; /这里比较的依据是看size
}
return super.equals(obj);
}
}
public class AppleCar extends Car {
private int count;
public AppleCar(int size, int count) {
super(size);
this.count = count;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if(obj instanceof AppleCar) {
AppleCar ac = (AppleCar)obj;
return ac.count==count;
}
return super.equals(obj);//这里会调用父类的Car的equal比较规则,
//虽然车的类型不同,但是车的size是否一致,作为最终判断依据
//return false;则这里就表示不是同一种类型的就是不相等
}
}
Car c= new Car(23);
Car c1 =new AppleCar(23,15);
System.out.println(c.equals(c1));//true
System.out.println(c1.equals(c));//ture
出现这样的问题就是在于instanceof 违法了对称性,你只能顺着实例对象的方向比较。从而我们在不是实例对象的时候,调用父类的equals来比较。
为啥重写equals要重写hashcode
hashCode的意思就是散列码,也就是哈希码。在java中,我们可以使用hashCode()来获取对象的哈希码,其值就是对象的存储地址,hashCode()方法出自Object。
String s = "ok";
String t = new String("ok");
System.out.println(s.hashCode()+" "+t.hashCode());//hashcode的值一样。
String 方法的Hashcode方法
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
我们可以看出,字符串s与t拥有相同的散列码,这是因为字符串的散列码是由内容导出的,它重写了Object的Hashcode方法。Object类默认的hashCode方法计算出来的对象存储地址,所以不同的对象,它的地址肯定不一样。
equals和Hashcode
和hashcode重写的equals方法应该是比较两个对象的值是否相等。
String a = new String("a");
String b = new String("a");
System.out.println(a.equals(b));//true
System.out.println(a==b);//false
System.out.println(a.hashCode());//97
System.out.println(b.hashCode());//97
由这个例子可以看出两个对象的equals(比较的是内容)相等,这两个对象的hashcode,必然相等。如果是同一个对象,那肯定内容相等,所以说必须要覆盖equals,比较内容。
- 如果两个对象通过调用equals方法是相等的,那么这两个对象调用hashCode方法必须返回相同的整数。
- 两个对象的hashcode相等。并不代表这两个对象equals相等,通过哈希函数产生的hashcode会1产生一个等价类,在这个等价类中的hashcode值都相等。
- 如果两个对象通过调用equals方法是不相等的,不要求这两个对象调用hashCode方法必须返回不同的整数。但是程序员应该意识到对不同的对象产生不同的hash值可以提供哈希表的性能。
Map map = new HashMap<>();
String s="ok";
String s1 = "ok";
map.put(s, "one");
System.out.println(map.get(s1));//one
System.out.println(map.get(s));//one
Car c = new Car();
Car c1 = new Car();
map.put(c,"kkk");
System.out.println(map.get(c));//kkk
System.out.println(map.get(c1));//null
这里在最后map取值,是根据key的hashcode,来取值,String因为重写了hashcode方法,是根据内容去计算的,所以内容相等,即hashcode相等;而Car的hashcode默认是object,即就是根据对象地址,两个不同的对象,hashcode肯定不一样,所以无法获取值。
上面的方法我们可以通过重写hashcode。我们在重写equals的时候,一定要重写hashcode方法。
重写equals()中getClass与instanceof的区别
一般都是推荐使用 getClass 来进行类型判断,保证是同一个类的对象。但是有些时候还是会用到instanceof,这个主要看需求。
参考引用:Java核心技术基础知识
推荐阅读
-
复习equals、hashCode的知识点
-
Object:所有类的超类--equals() & hashCode()、== 和 equals.() 两者均可用于比较两个对象是否相等
-
说说hashCode()和equals()的相关问题
-
散列集合的HashCode&Equals
-
(转)hashCode与equals的区别与联系 博客分类: java
-
Object类的equals方法和hashCode方法 博客分类: Java基础 javaequalshashCode
-
为什么在重写 equals方法的同时必须重写 hashcode方法
-
为什么在重写 equals方法的同时必须重写 hashcode方法
-
java集合——Java中的equals和hashCode方法详解
-
java集合——Java中的equals和hashCode方法详解