原文链接:https://www.cnblogs.com/dolphin0520/p/3681042.html
上面详细讲了equals和hashCode,以下是我的总结。
1、equals方法
public boolean equals(Object obj) {
return (this == obj);
}
上面是object类的equals方法,可以看到object中是直接比较的地址值
某些情况下我们可能并不想比较地址值,而是比较两个对象的属性值是否相等,如果属性值都相等那么就认为是同一个对象!(额,并不会有这种情况!反正我是没遇到过。)
大家都知道我们只需要重写equals方法就可以了。
class People{
private String name;
private int age;
public People(String name,int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return this.name.equals(((People)obj).name) && this.age== ((People)obj).age;
}
}
public class Main {
public static void main(String[] args) {
People p1 = new People("Jack", 12);
System.out.println(p1.hashCode());
HashMap<People, Integer> hashMap = new HashMap<People, Integer>();
hashMap.put(p1, 1);
System.out.println(hashMap.get(new People("Jack", 12)));//null
}
}
上面的代码重写了People类的equals方法,然后创建了一个name是Jack年龄是12的对象当做key,存储进了map中,但是get方法重新new了一个name是Jack年龄是12的对象,取值是却没有拿到1。
这段代码本来的意愿是想这段代码输出结果为“1”,但是事实上它输出的是“null”。为什么呢?原因就在于重写equals方法的同时忘记重写hashCode方法。
虽然通过重写equals方法使得逻辑上姓名和年龄相同的两个对象被判定为相等的对象(跟String类类似),但是要知道默认情况下,hashCode方法是将对象的存储地址进行映射。那么上述代码的输出结果为“null”就不足为奇了。原因就在于重写equals方法的同时忘记重写hashCode方法。
System.out.println(hashMap.get(new People("Jack", 12)));这句中的new People("Jack", 12)生成的是两个对象,它们的存储地址肯定不同。
map中get方法比较key是否相同时,首先比较hashcode是否相等,如果hashcode相等,那么再去调用equals方法是否相等,由于没有重写hashCode方法,默认hashCode方法与内存地址相关,hash算法根据内存地址获得一个hashCode,这里new的是两个people对象,那么当然hashcode码不一样,key不一样那么自然拿不到对应的value值!
因此如果想上述代码输出结果为“1”,很简单,只需要重写hashCode方法,让equals方法和hashCode方法始终在逻辑上保持一致性。(这也就是为什么大家都说重写equals方法那么一定要重写hashCode方法,因为java中判断一个对象是否相等是根据hashcode和equals方法一起判断的,这两个方法的逻辑上一定要保持一致。)
下面这段话摘自Effective Java一书:
在程序执行期间,只要equals方法的比较操作用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法必须始终如一地返回同一个整数。
如果两个对象根据equals方法比较是相等的,那么调用两个对象的hashCode方法必须返回相同的整数结果。
如果两个对象根据equals方法比较是不等的,则hashCode方法不一定得返回不同的整数。
其实上面的代码不重写hashCode方法也有可能是能取到值的,因为java中不同的内存地址,根据hash算法也有可能算出来是相同的code码,但是可能性比较小。
最终总结:
因此有人会说,可以直接根据hashcode值判断两个对象是否相等吗?肯定是不可以的,因为不同的对象可能会生成相同的hashcode值。虽然不能根据hashcode值判断两个对象是否相等,但是可以直接根据hashcode值判断两个对象不等,如果两个对象的hashcode值不等,则必定是两个不同的对象。如果要判断两个对象是否真正相等,必须通过equals方法。
也就是说对于两个对象,如果调用equals方法得到的结果为true,则两个对象的hashcode值必定相等;(equals为true,那么地址值相同,内存地址值相同,那么hash算法算出的hashcode也一定相同。)
如果equals方法得到的结果为false,则两个对象的hashcode值不一定不同;(不同的内存地址值,hash算法也有较小可能得出相同的hashcode)
如果两个对象的hashcode值不等,则equals方法得到的结果必定为false;(由第一条可以得出这条结论)
如果两个对象的hashcode值相等,则equals方法得到的结果未知。
3、clone
1、对象浅拷贝
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
TestA ta=new TestA();
TestA ta1=(TestA) ta.clone();
System.out.println(ta1);
}
}
class TestA implements Cloneable{
public String name = "father";
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "TestA [name=" + name + "]";
}
}
实现Cloneable接口,并且重写object类中的clone方法,即可。
浅拷贝:只将基本类型和String类型进行了拷贝。而所有的引用对象仍然指向原来的对象。换言之,浅拷贝不复制引用对象。
2、深拷贝
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
TestA ta=new TestA();
ta.name="aaa";
ta.testB=new TestB(10);
TestA ta1=(TestA) ta.clone();
ta1.name="b";
ta1.testB.age=20;
System.out.println(ta1);
System.out.println(ta);
}
}
class TestA implements Cloneable{
public String name;
public TestB testB;
@Override
protected Object clone() throws CloneNotSupportedException {
TestA o = (TestA) super.clone();
o.testB = (TestB) o.testB.clone();
return o;
}
@Override
public String toString() {
return "TestA [name=" + name + ", testB=" + testB + "]";
}
}
class TestB implements Cloneable{
public int age;
public TestB(int age) {
this.age=age;
}
@Override
public String toString() {
return "TestB [age=" + age + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
网友评论