详解hashCode()和equals()的本质区别和联系
在学习java,根据视频做实例的过程中,对equals和hashcode两个方法理解稍微深刻一点,主要是它们两个很容易混淆,容易出错,自己又通过网上的资料学习,和大家分享
equals()方法
equals是object类提供的方法之一,众所周知,每一个java类都继承自object类,所以说每一个对象都有equals这个方法。而我们在用这个方法时却一般都重写这个方法,why?
先看一个object类中equals()方法的源代码:
public boolean equals(object obj) { return (this == obj); }
从这个方法中可以看出,只有当一个实例等于它本身的时候,equals()才会返回true值。通俗地说,此时比较的是两个引用是否指向内存中的同一个对象,也可以称做是否实例相等。而我们在使用equals()来比较两个指向值对象的引用的时候,往往希望知道它们逻辑上是否相等,而不是它们是否指向同一个对象——这就是我们通常重写这个方法的原因。
strings1 = new string(“kvill”),string s2 = new string(“kvill”);s1.equals(s2)为ture,
说明string类中已经重写了equals()方法,如果不重写equals()方法,那么s1.equals(s2)默认比较两个对象所指向的内存地址是否相同,返回值必然为false。
当然接下来我们要改写equals()方法,必须要遵守通用约定。来自java.lang.object的规范,equals方法实现了等价关系,以下是要求遵循的5点:
1.自反性:对于任意的引用值x,x.equals(x)一定为true。
2.对称性:对于任意的引用值x 和 y,当x.equals(y)返回true时,y.equals(x)也一定返回true。
3.传递性:对于任意的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也一定返回true。
4. 一致性:对于任意的引用值x 和y,如果用于equals比较的对象信息没有被修改,多次调用x.equals(y)要么一致地返回true,要么一致地返回false。
5.非空性:对于任意的非空引用值x,x.equals(null)一定返回false。
hashcode()方法
hashcode()这个方法也是从object类中继承过来的,在object类中定义如下:
public native int hashcode();
hashcode()返回该对象的哈希码值,该值通常是一个由该对象的内部地址转换而来的整数,它的实现主要是为了提高哈希表(例如java.util.hashtable提供的哈希表)的性能。
必须铭记:在每个重写了equals方法的类中,你必须也要重写hashcode方法。如果不这样做的话,就会违反object.hashcode的通用约定,从而导致该类无法与所有基于散列值(hash)的集合类结合在一起正常运行。
hashcode()的返回值和equals()的关系如下:
如果x.equals(y)返回“true”,那么x和y的hashcode()必须相等。
如果x.equals(y)返回“false”,那么x和y的hashcode()有可能相等,也有可能不等。
public class testequals { public static void main(string args[]) { student s1 = new student("张一", 6); student s2 = new student("张一", 6); if (s1.equals(s2)) { system.out.println("相同 s1的代码:" + s1.hashcode() + " s2的代码:" + s2.hashcode()); } else { system.out.println("不相同"); } } } class student { private int age; private string name; public student() { } public student(string name, int age) { this.age = age; this.name = name; } public int getage() { return age; } public void setage(int age) { this.age = age; } public string getname() { return name; } public void setname(string name) { this.name = name; } public int hashcode() { return (this.name.hashcode() + this.age) * 31; } public boolean equals(object obj) { boolean result = false; if (obj == null) { result = false; } if (this == obj) { result = true; } if (obj instanceof student) { student stu = (student) obj; if (stu.getname().equals(this.name) && stu.getage() == (this.age)) { result = true; } } else { result = false; } return result; } }
详细分析
equals()是判读两个set是否相等[前提是equals()在类中被覆盖]。==决定引用值是否指向同一对象。
1、当向集合set中增加对象时,首先计算要增加对象的hashcode码,根据该值来得到一个位置来存放当前的对象,当在该位置没有一个对象存在的话,那么集合set认为该对象在集合中不存在,直接增加进去。如果在该位置有一个对象的话,接着将准备增加到集合中的对象与该位置上的对象进行equals方法比较,如果该equals方法返回false,那么集合认为集合中不存在该对象,再进行一次散列,将该对象放到散列后计算出的新地址里,如果equals方法返回true,那么集合认为集合中已经存在该对象了,不会再将该对象增加到集合中了。
2、当重写equals方法时,必须要重写hashcode方法。在java的集合中,判断两个对象是否相等的规则是:
1),判断两个对象的hashcode是否相等
如果不相等,认为两个对象也不相等,完毕 ; 如果相等,转入2
2),判断两个对象用equals运算是否相等
如果不相等,认为两个对象也不相等
如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)
可见hashcode()相等时,equals()方法也可能不等。
public static void main(string args[]){ string s1=new string("zhaoxudong"); //此语句创建了两个对象,一个是字符串对象“zhaoxudong”(存放于栈中的字面量),另一个是new后在堆中产生的对象。详细见下面的四.4 string s2=new string("zhaoxudong"); //上述两条语句一共是产生了三个对象,因为栈中只有产生了一个对象。 system.out.println(s1==s2);//false system.out.println(s1.equals(s2));//true system.out.println(s1.hashcode());//s1.hashcode()等于s2.hashcode() ,指向同一内存的引用 system.out.println(s2.hashcode()); //equals和hashcode方法只用于两个对象的比较和容器中,与对象的创建没有关系 set hashset=new hashset(); hashset.add(s1); hashset.add(s2); /*在添加s1,s2时, hashset认为s1和s2是相等的,所以让s2覆盖了s1;*/ iterator it=hashset.iterator(); while(it.hasnext()){ system.out.println(it.next()); } //最后在while循环的时候只打印出了一个”zhaoxudong”。
这是因为string类已经重写了equals()方法和hashcode()方法。
但是看下面的程序:
>public class hashsettest { public static void main(string[] args) { hashset hs=new hashset(); hs.add(new student(1,"zhangsan")); hs.add(new student(2,"lisi")); hs.add(new student(3,"wangwu")); hs.add(new student(1,"zhangsan")); iterator it=hs.iterator(); while(it.hasnext()){ system.out.println(it.next()); } } } class student { int num; string name; student(int num,string name) { this.num=num; this.name=name; } public string tostring() { return num+":"+name; } }
输出结果为:
1:zhangsan 1:zhangsan 3:wangwu 2:lisi
为什么hashset添加了相等的元素呢?
这是不是和hashset的原则违背了呢?回答:没有因为在根据hashcode()对两次建立newstudent(1,"zhangsan")对象进行比较时,生成的是不同的哈希码值,所以hashset把他当作不同的对象对待了,当然此时的equals()方法返回的值也不等。那么为什么会生成不同的哈希码值呢?原因就在于我们自己写的student类并没有重新自己的hashcode()和equals()方法
所以在比较时,是继承的object类中的hashcode()方法,它是一个本地方法,比较的是对象的地址(引用地址),使用new方法创建对象,两次生成的当然是不同的对象了,造成的结果就是两个对象的hashcode()返回的值不一样。那么怎么解决这个问题呢?
原因是:在student类中重新hashcode()和equals()方法。
class student{ int num; string name; student(int num,string name){ this.num=num; this.name=name; } public int hashcode(){ //重写hashcode的方法 return num*name.hashcode(); } public boolean equals(object o) { student s=(student)o; return num==s.num && name.equals(s.name); //&&的优先级比==低,所以前面不必加括号 } public string tostring(){return num+":"+name; } }
根据重写的方法,即便两次调用了new student(1,"zhangsan"),我们在获得对象的哈希码时,根据重写的方法hashcode(),获得的哈希码肯定是一样的。所以运行修改后的程序时,我们会看到重复元素的问题已经消除。
总结
这块知识比较容易出错,理解一定要深刻,多多的实践会对原理与定义理解的更加的深刻。有什么问题可以随时留言,小编会及时回复大家的。感谢大家对本站的支持。
上一篇: php实现文件上传及头像预览功能
推荐阅读
-
详解hashCode()和equals()的本质区别和联系
-
equals()方法和hashCode()的使用
-
如果使用Object作为HashMap的Key,为什么要重写hashcode和equals
-
HashMap中的key为什么不能为可变对象(除非重写它的hashcode方法和equals方法)
-
当集合中的对象要去重时,为什么要重写hashCode和equals方法
-
Object:所有类的超类--equals() & hashCode()、== 和 equals.() 两者均可用于比较两个对象是否相等
-
说说hashCode()和equals()的相关问题
-
Object类的equals方法和hashCode方法 博客分类: Java基础 javaequalshashCode
-
java equals和==的区别详解
-
java集合——Java中的equals和hashCode方法详解