1、1如果函数返回类型不同,子类的同名函数不能继承父类的同名函数c+ 如果返回值类型是基本数据类型,必须相同,不然编译不通过。如果返回值类型是类类型,可以不相同hashcode 方法浅析有许多人学了很长时间的 Java,但一直不明白 hashCode 方法的作用,我来解释一下吧。首先,想要明白 hashCode 的作用,你必须要先知道 Java 中的集合。总的来说,Java 中的集合(Collection)有两类,一类是 List,再有一类是 Set。你知道它们的区别吗?前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。那么这里就有一个比较严重的问题了:要想保证元素不重复,可两
2、个元素是否重复应该依据什么来判断呢?这就是Object.equals 方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有 1000 个元素,那么第 1001 个元素加入集合时,它就要调用 1000次 equals 方法。这显然会大大降低效率。 于是,Java 采用了哈希表的原理。哈希(Hash)实际上是个人名,由于他提出一哈希算法的概念,所以就以他的名字命名了。哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。如果详细讲解哈希算法,那需要更多的文章篇幅,我在这里就不介绍了。初学者可以这样理解,ha
3、shCode 方法实际上返回的就是对象存储的物理地址(实际可能并不是)。 这样一来,当集合要添加新的元素时,先调用这个元素的 hashCode 方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的 equals 方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用 equals 方法的次数就大大降低了,几乎只需要一两次。 所以,Java 对于 eqauls 方法和 hashCode 方法是这样规定的:1、如果两个对象相同,那么它
4、们的 hashCode 值一定要相同;2、如果两个对象的hashCode 相同,它们并不一定相同 上面说的对象相同指的是用 eqauls 方法比较。 你当然可以不按要求去做了,但你会发现,相同的对象可以出现在 Set 集合中。同时,增加新元素的效率会大大下降。java 中 hashcode()和 equals()的详解1. 首先 equals()和 hashcode()这两个方法都是从 object 类中继承过来的。 equals()方法在 object 类中定义如下: public boolean equals(Object obj) return (this = obj); 很明显是对两个
5、对象的地址值进行的比较(即比较引用是否相同) 。但是我们必需清楚,当String 、Math、还有 Integer、Double。 。 。 。等这些封装类在使用 equals()方法时,已经覆盖了 object 类的 equals()方法。比如在 String 类中如下: 2public boolean equals(Object anObject) if (this = anObject) return true; if (anObject instanceof String) String anotherString = (String)anObject; int n = count; i
6、f (n = anotherString.count) char v1 = value; char v2 = anotherString.value; int i = offset; int j = anotherString.offset; while (n- != 0) if (v1i+ != v2j+) return false; return true; return false; 很明显,这是进行的内容比较,而已经不再是地址的比较。依次类推Double、Integer、Math。 。 。 。等等这些类都是重写了 equals()方法的,从而进行的是内容的比较。当然了基本类型是进行值的
7、比较,这个没有什么好说的。 我们还应该注意,Java 语言对 equals()的要求如下,这些要求是必须遵循的: 对称性:如果 x.equals(y)返回是“true” ,那么 y.equals(x)也应该返回是“true” 。 反射性:x.equals(x)必须返回是“true” 。 类推性:如果 x.equals(y)返回是“true” ,而且 y.equals(z)返回是“true” ,那么 z.equals(x)也应该返回是“true” 。 还有一致性:如果 x.equals(y)返回是“true” ,只要 x 和 y 内容一直不变,不管你重复x.equals(y)多少次,返回都是“t
8、rue” 。 任何情况下,x.equals(null) ,永远返回是“false” ;x.equals( 和 x 不同类型的对象) 永远返回是“false” 。 以上这五点是重写 equals()方法时,必须遵守的准则,如果违反会出现意想不到的结果,请大家一定要遵守。 2. 其次是 hashcode() 方法,在 object 类中定义如下: public native int hashCode(); 说明是一个本地方法,它的实现是根据本地机器相关的。当然我们可以在自己写的类中覆盖 hashcode()方法,比如 String、Integer、Double。 。 。 。等等这些类都是覆盖了 h
9、ashcode()方法的。例如在 String 类中定义的 hashcode()方法如下: public int hashCode() int h = hash; if (h = 0) int off = offset; 3char val = value; int len = count; for (int i = 0; i len; i+) h = 31*h + valoff+; hash = h; return h; 解释一下这个程序(String 的 API 中写到): s0*31(n-1) + s1*31(n-2) + . + sn-1 使用 int 算法,这里 si 是字符串的第
10、i 个字符,n 是字符串的长度, 表示求幂。 (空字符串的哈希码为 0。 ) 3.这里我们首先要明白一个问题: equals()相等的两个对象,hashcode()一定相等; equals()不相等的两个对象,却并不能证明他们的 hashcode()不相等。换句话说,equals()方法不相等的两个对象,hashcode()有可能相等。 (我的理解是由于哈希码在生成的时候产生冲突造成的) 。 反过来:hashcode() 不等,一定能推出 equals()也不等;hashcode()相等,equals() 可能相等,也可能不等。解释下第 3 点的使用范围,我的理解是在 object、Strin
11、g 等类中都能使用。在object 类中,hashcode()方法是本地方法,返回的是对象的地址值,而 object 类中的 equals()方法比较的也是两个对象的地址值,如果 equals()相等,说明两个对象地址值也相等,当然hashcode()也就相等了;在 String 类中,equals()返回的是两个对象内容的比较,当两个对象内容相等时, Hashcode()方法根据 String 类的重写(第 2 点里面已经分析了)代码的分析,也可知道hashcode()返回结果也会相等。以此类推,可以知道 Integer、Double 等封装类中经过重写的 equals()和 hashcod
12、e()方法也同样适合于这个原则。当然没有经过重写的类,在继承了object 类的 equals()和 hashcode()方法后,也会遵守这个原则。 4.谈到 hashcode()和 equals()就不能不说到 hashset,hashmap,hashtable 中的使用,具体是怎样呢,请看如下分析: Hashset 是继承 Set 接口,Set 接口又实现 Collection 接口,这是层次关系。那么 hashset 是根据什么原理来存取对象的呢? 在 hashset 中不允许出现重复对象,元素的位置也是不确定的。在 hashset 中又是怎样判定元素是否重复的呢?这就是问题的关键所在,
13、经过一下午的查询求证终于获得了一点启示,和大家分享一下,在 java 的集合中,判断两个对象是否相等的规则是: 1),判断两个对象的 hashCode 是否相等 如果不相等,认为两个对象也不相等,完毕 如果相等,转入 2) (这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们这里将其做为必需的。后面会重点讲到这个问题。 ) 2),判断两个对象用 equals 运算是否相等 4如果不相等,认为两个对象也不相等 如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键) 为什么是两条准则,难道用第一条不行吗?不行,因为前面已经说
14、了,hashcode()相等时,equals()方法也可能不等,所以必须用第 2 条准则进行限制,才能保证加入的为非重复元素。比如下面的代码: public static void main(String args) String s1=new String(“zhaoxudong“); 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()
15、等于 s2.hashcode() System.out.println(s2.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 循环的时候只打印出了一个”
16、zhaoxudong” 。 输出结果为:false true -967303459 -967303459 这是因为 String 类已经重写了 equals()方法和 hashcode()方法,所以在根据上面的第 1.2 条原则判定时,hashset 认为它们是相等的对象,进行了重复添加。 但是看下面的程序: import java.util.*; public class HashSetTest public static void main(String args) HashSet hs=new HashSet(); hs.add(new Student(1,“zhangsan“); hs
17、.add(new Student(2,“lisi“); hs.add(new Student(3,“wangwu“); hs.add(new Student(1,“zhangsan“); Iterator it=hs.iterator(); 5while(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 nu
18、m+“:“+name; 输出结果为: 1:zhangsan 1:zhangsan 3:wangwu 2:lisi 问题出现了,为什么 hashset 添加了相等的元素呢,这是不是和 hashset 的原则违背了呢?回答是:没有 因为在根据 hashcode()对两次建立的 new Student(1,“zhangsan“)对象进行比较时,生成的是不同的哈希码值,所以 hashset 把他当作不同的对象对待了,当然此时的 equals()方法返回的值也不等(这个不用解释了吧) 。那么为什么会生成不同的哈希码值呢?上面我们在比较s1 和 s2 的时候不是生成了同样的哈希码吗?原因就在于我们自己写的
19、 Student 类并没有重新自己的 hashcode()和 equals()方法,所以在比较时,是继承的 object 类中的 hashcode()方法,呵呵,各位还记得 object 类中的 hashcode()方法比较的是什么吧! 它是一个本地方法,比较的是对象的地址(引用地址) ,使用 new 方法创建对象,两次生成的当然是不同的对象了(这个大家都能理解吧。 。 。 ) ,造成的结果就是两个对象的hashcode()返回的值不一样。所以根据第一个准则,hashset 会把它们当作不同的对象对待,自然也用不着第二个准则进行判定了。那么怎么解决这个问题呢? 答案是:在 Student 类中
20、重新 hashcode()和 equals()方法。 例如: class Student int num; String name; Student(int num,String name) 6 this.num=num; this.name=name; public int hashCode() return num*name.hashCode(); public boolean equals(Object o) Student s=(Student)o; return num=s.num public String toString() return num+“:“+name; 根据重写的
21、方法,即便两次调用了 new Student(1,“zhangsan“),我们在获得对象的哈希码时,根据重写的方法 hashcode(),获得的哈希码肯定是一样的(这一点应该没有疑问吧) 。 当然根据 equals()方法我们也可判断是相同的。所以在向 hashset 集合中添加时把它们当作重复元素看待了。所以运行修改后的程序时,我们会发现运行结果是: 1:zhangsan 3:wangwu 2:lisi 可以看到重复元素的问题已经消除。 关于在 hibernate 的 pojo 类中,重新 equals()和 hashcode()的问题: 1),重点是 equals,重写 hashCode
22、只是技术要求(为了提高效率) 2),为什么要重写 equals 呢,因为在 java 的集合框架中,是通过 equals 来判断两个对象是否相等的 3),在 hibernate 中,经常使用 set 集合来保存相关对象,而 set 集合是不允许重复的。我们再来谈谈前面提到在向 hashset 集合中添加元素时,怎样判断对象是否相同的准则,前面说了两条,其实只要重写 equals()这一条也可以。 但当 hashset 中元素比较多时,或者是重写的 equals()方法比较复杂时,我们只用 equals()方法进行比较判断,效率也会非常低,所以引入了 hashcode()这个方法,只是为了提高效
23、率,但是我觉得这是非常有必要的(所以我们在前面以两条准则来进行 hashset 的元素是否重复的判断) 。 比如可以这样写: public int hashCode() return 1;/等价于 hashcode 无效 这样做的效果就是在比较哈希码的时候不能进行判断,因为每个对象返回的哈希码都是1,每次都必须要经过比较 equals()方法后才能进行判断是否重复,这当然会引起效率的大大降低。 我有一个问题,如果像前面提到的在 hashset 中判断元素是否重复的必要方法是 equals()方7法(根据网上找到的观点) ,但是这里并没有涉及到关于哈希表的问题,可是这个集合却叫hashset,这
24、是为什么? 我想,在 hashmap,hashtable 中的存储操作,依然遵守上面的准则。所以这里不再多说。这些是今天看书,网上查询资料,自己总结出来的,部分代码和语言是引述,但是千真万确是自己总结出来的。有错误之处和不详细不清楚的地方还请大家指出,我也是初学者,所以难免会有错误的地方,希望大家共同讨论。 1. =是用来比较两个变量(基本类型和对象类型)的值是否相等的, 如果两个变量是基本类型的,那很容易,直接比较值就可以了。如果两个变量是对象类型的,那么它还是比较值,只是它比较的是这两个对象在栈中的引用(即地址)。 对象是放在堆中的,栈中存放的是对象的引用(地址)。由此可见=是对栈中的值进
25、行比较的。如果要比较堆中对象的内容是否相同,那么就要重写equals 方法了。 2. Object 类中的 equals 方法就是用=来比较的,所以如果没有重写 equals方法,equals 和=是等价的。 通常我们会重写 equals 方法,让 equals 比较两个对象的内容,而不是比较对象的引用(地址)因为往往我们觉得比较对象的内容是否相同比比较对象的引用(地址)更有意义。 3. Object 类中的 hashCode 是返回对象在内存中地址转换成的一个 int 值(可以就当做地址看)。所以如果没有重写 hashCode 方法,任何对象的 hashCode都是不相等的。通常在集合类的时
26、候需要重写 hashCode 方法和 equals 方法,因为如果需要给集合类(比如:HashSet)添加对象,那么在添加之前需要查看给集合里是否已经有了该对象,比较好的方式就是用 hashCode。 4. 注意的是 String、Integer、Boolean、Double 等这些类都重写了 equals和 hashCode 方法,这两个方法是根据对象的内容来比较和计算 hashCode 的。(详细可以查看 jdk 下的 String.java 源代码),所以只要对象的基本类型值相同,那么 hashcode 就一定相同。 5. equals()相等的两个对象,hashcode()一般是相等的
27、,最好在重写 equals()方法时,重写 hashcode()方法; equals()不相等的两个对象,却并不能证明他们的 hashcode()不相等。换句话说,equals()方法不相等的两个对象,hashcode()有可能相等。 反过来:hashcode()不等,一定能推出 equals()也不等;hashcode()相等,equals()可能相等,也可能不等。在 object 类中,hashcode()方法是本地方法,返回的是对象的引用(地址值),而 object 类中的 equals()方法比较的也是两个对象的引用(地址值),如果 equals()相等,说明两个对象地址值也相等,当然
28、 hashcode()也就相等了。 以下是测试代码。 Java 代码 1. public class Equals_HashCode 2. public static void main(String args) 3. String a = new String(“str“); 4. String b = new String(“str“); 85. System.out.println(a=b); 6. System.out.println(a.equals(b); 7. System.out.println(a.hashCode(); 8. System.out.println(b.has
29、hCode(); 9. / 输出 false true 114225 114225 10. class A 11. String str; 12. int i; 13. public A(String str, int i) 14. super(); 15. this.str = str; 16. this.i = i; 17. 18. 19. A aA = new A(“str“,1); 20. A bA = new A(“str“,1); 21. System.out.println(aA=bA); 22. System.out.println(aA.equals(bA); 23. Sys
30、tem.out.println(aA.hashCode(); 24. System.out.println(bA.hashCode(); 25. / 输出 false false 6413875 21174459 26. class B 27. String str; 28. public B(String str) 29. this.str = str; 30. 31. 32. B aB = new B(“str“); 33. B bB = new B(“str“); 34. System.out.println(aB=bB); 35. System.out.println(aB.equal
31、s(bB); 36. System.out.println(aB.hashCode(); 37. System.out.println(bB.hashCode(); 38. / 输出 false false 827574 17510567 39. class C 40. int i; 41. public C(int i) 42. this.i = i; 43. 44. 45. C aC = new C(1); 46. C bC = new C(1); 47. System.out.println(aC=bC); 48. System.out.println(aC.equals(bC); 94
32、9. System.out.println(aC.hashCode(); 50. System.out.println(bC.hashCode(); 51. /输出 false false 27744459 28737396 52. 53. 54. 55. java 中 equal 和=的比较 java 中 equals 方法和“=”的区别:equals 方法是 java.lang.Object 类的方法。有两种用法说明:(1)对于字符串变量来说,使用“=”和“equals()”方法比较字符串时,其比较方法不同。“=”比较两个变量本身的值,即两个对象在内存中的首地址。“equals()”比较字
33、符串中所包含的内容是否相同。比如:String s1,s2,s3 = “abc“, s4 =“abc“ ;s1 = new String(“abc“);s2 = new String(“abc“);那么:s1=s2 是 false /两个变量的内存地址不一样,也就是说它们指向的对象不 一样,故不相等。s1.equals(s2) 是 true /两个变量的所包含的内容是 abc,故相等。注意(1):10如果: StringBuffer s1 = new StringBuffer(“a“);StringBuffer s2 = new StringBuffer(“a“);结果: s1.equals(
34、s2) /是 false解释:StringBuffer 类中没有重新定义 equals 这个方法,因此这个方法就来自 Object 类,而 Object 类中的 equals 方法是用来比较“地址”的,所以等于 false.注意(2):对于 s3 和 s4 来说,有一点不一样要引起注意,由于 s3 和 s4 是两个字符串常量所生成的变量,其中所存放的内存地址是相等的,所以 s3=s4 是 true(即使没有 s3=s4 这样一个赋值语句)(2)对于非字符串变量来说,“=“和“equals“方法的作用是相同的都是用来比较其对象在堆内存的首地址,即用来比较两个引用变量是否指向同一个对象。比如:cl
35、ass AA obj1 = new A();A obj2 = new A();那么:obj1=obj2 是 falseobj1.equals(obj2)是 false但是如加上这样一句:obj1=obj2;那么 obj1=obj2 是 true11obj1.equals(obj2) 是 true总之:equals 方法对于字符串来说是比较内容的,而对于非字符串来说是比较其指向的对象是否相同的。= 比较符也是比较指向的对象是否相同的也就是对象在对内存中的的首地址。String 类中重新定义了 equals 这个方法,而且比较的是值,而不是地址。所以是 true。关于 equals 与=的区别从以
36、下几个方面来说:(1)如果是基本类型比较,那么只能用=来比较,不能用 equals比如:public class TestEquals public static void main(String args)int a = 3;int b = 4;int c = 3;System.out.println(a = b);/结果是 falseSystem.out.println(a = c);/结果是 trueSystem.out.println(a.equals(c);/错误,编译不能通过,equals 方法/不能运用与基本类型的比较(2)对于基本类型的包装类型,比如Boolean、Charac
37、ter、Byte、Shot、Integer、Long、Float、Double 等的引用12变量,=是比较地址的,而 equals 是比较内容的。比如:public class TestEquals public static void main(String args) Integer n1 = new Integer(30);Integer n2 = new Integer(30);Integer n3 = new Integer(31);System.out.println(n1 = n2);/结果是 false 两个不同的 Integer 对象,故其地址不同,System.out.pr
38、intln(n1 = n3);/那么不管是 new Integer(30)还是 new Integer(31) 结果都显示 falseSystem.out.println(n1.equals(n2);/结果是 true 根据 jdk 文档中的说明,n1 与 n2 指向的对象中的内容是相等的,都是 30,故 equals 比较后结果是trueSystem.out.println(n1.equals(n3);/结果是 false 因对象内容不一样,一个是 30 一个是 31这是 Integer 的实例,如果是其他的比如 Double、Character、Float 等也一样。(3)注意:对于 St
39、ring(字符串)、StringBuffer(线程安全的可变字符序列)、StringBuilder(可变字符序列)这三个类作进一步的说明。(a)首先,介绍 String 的用法,请看下面的实例:public class TestEquals public static void main(String args) String s1 = “123“;String s2 = “123“;13String s3 = “abc“;String s4 = new String(“123“);String s5 = new String(“123“);String s6 = new String(“ab
40、c“);System.out.println(s1 = s2);/(1)trueSystem.out.println(s1.equals(s2);/(2)trueSystem.out.println(s1 = s3);/(3)flaseSystem.out.println(s1.equals(s3);/(4)flaseSystem.out.println(s4 = s5);/(5)flaseSystem.out.println(s4.equals(s5);/(6)trueSystem.out.println(s4 = s6);/(7)flaseSystem.out.println(s4.equ
41、als(s6);/(8)flaseSystem.out.println(s1 = s4);/(9)falseSystem.out.println(s1.equals(s4);/(10)true答案解释:s1 与 s2 分别指向由字符串常量”123” 创建的对象,在常量池中,只有一个对象,内容为 123,有两个引用 s1 和 s2 指向这个对象,故这两个引用变量所指向的地址是相同的,因而(1)处的运行结果为 true,又因为s1.equals(s2)是比较 s1 和 s2 所指向的对象的内容是否相等,而我们知道这两个对象的内容都是字符串常量”123”,故标记(2)处的运行结果是 true。用同样
42、的方法分析,s1 和 s3 所指向的对象不一样,内容也不一样,故标记(3)和(4)处运行结果是 false。再看看 s4 和 s5,这两个引用变量所指向的对象的内容都是一样的(内容都是123),但是这两个对象是用 new 操作符创建处类的,是在内存中分配两块空间14给这两个对象的,因而这两个对象的内存地址不一样,故事两个不同的对象,标记(5)处的 s4 = s5 运行结果为 false,但是内容一样,故标记(6)处的s4.equals(s5)运行结果为 true。同理,s4 和 s6 所指向的对象地址不同,内容也不相同。故标记(7)(8)处运行结果为 false。s1 和 s4 分别指向两个不
43、同的对象(之所以这样称呼,是因为这两个对象在内存中的地址不相同,故而对象不相同),故标记为(9)处的 s1 = s4 运行结果为 false,而标记为(10)处的 s1.equals(s4)运行结果疑问:乍一看结果,有点惊讶,为什么不是 true 呢,不是说 equals 方法是比较内容的吗?解释:不错,如果在新类中被覆盖了 equals 方法,就可以用来比较内容的。但是在上面的例子中类 Value 并没有覆盖 Object 中的 equals 方法,而是继承了该方法,因此它就是被用来比较地址的,又 v1 和 v2 的所指向的对象不相同,故标记(1)处的 v1.equals(v2)运行结果为 false,标记为(2)处的 v1 = v2 运行结果也为 false。