http://www.iteye.com/topic/257191
-----------------
今天下午研究了半天hashcode()和equals()ҎQ终于有了一点点的明白,写下来与大家分nQzhaoxudong 2008.10.23?1.36Q?
1. 首先equals()和hashcode()q两个方法都是从objectcMl承q来的?
equals()Ҏ在objectcM定义如下Q?
public boolean equals(Object obj) {
return (this == obj);
}
很明显是对两个对象的地址D行的比较Q即比较引用是否相同Q。但是我们必需清楚Q当String 、Math、还有Integer、Double。。。。等q些装cd使用equals()ҎӞ已经覆盖了objectcȝequalsQ)Ҏ。比 如在StringcM如下Q?
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
很明显,q是q行的内Ҏ较,而已l不再是地址的比较。依ơ类推Double、Integer、Math。。。。等{这些类都是重写了equals()Ҏ的,从而进行的是内容的比较。当然了基本cd是进行值的比较Q这个没有什么好说的?
我们q应该注意,Java语言对equals()的要求如下,q些要求是必遵循的Q?
• 对称性:如果x.equals(y)q回?#8220;true”Q那么y.equals(x)也应该返回是“true”?
• 反射性:x.equals(x)必须q回?#8220;true”?
• cL性:如果x.equals(y)q回?#8220;true”Q而且y.equals(z)q回?#8220;true”Q那么z.equals(x)也应该返回是“true”?
• q有一致性:如果x.equals(y)q回?#8220;true”Q只要x和y内容一直不变,不管你重复x.equals(y)多少ơ,q回都是“true”?
• M情况下,x.equals(null)Q永q返回是“false”Qx.equals(和x不同cd的对?永远q回?#8220;false”?
以上q五Ҏ重写equals()ҎӞ必须遵守的准则,如果q反会出现意想不到的l果Q请大家一定要遵守?
2. 其次是hashcode() ҎQ在objectcM定义如下Q?
public native int hashCode();
说明是一个本地方法,它的实现是根据本地机器相关的。当然我们可以在自己写的cM覆盖hashcode()ҎQ比如String?Integer、Double。。。。等{这些类都是覆盖了hashcode()Ҏ的。例如在StringcM定义的hashcode()Ҏ如下Q?
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
解释一下这个程序(String的API中写刎ͼQ?
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
使用 int 法Q这?s[i] 是字W串的第 i 个字W,n 是字W串的长度,^ 表示求幂。(I字W串的哈希码?0。)
3.q里我们首先要明白一个问题:
equals()相等的两个对象,hashcode()一定相{;
equalsQ)不相{的两个对象Q却q不能证明他们的hashcode()不相{。换句话_equals()Ҏ不相{的两个对象Qhashcode()有可能相{。(我的理解是由于哈希码在生成的时候生冲H造成的)?
反过来:hashcode()不等Q一定能推出equals()也不{;hashcode()相等Qequals()可能相等Q也可能不等。解?下第3点的使用范围Q我的理解是在object、String{类中都能用。在objectcMQhashcode()Ҏ是本地方法,q回的是对象?地址|而objectcM的equals()Ҏ比较的也是两个对象的地址|如果equals()相等Q说明两个对象地址g相等Q当?hashcode()也就相等了;在StringcMQequals()q回的是两个对象内容的比较,当两个对象内容相{时Q?
Hashcode()ҎҎStringcȝ重写Q第2炚w面已l分析了Q代码的分析Q也可知道hashcode()q回l果也会相等。以此类 推,可以知道Integer、Double{封装类中经q重写的equals()和hashcode()Ҏ也同样适合于这个原则。当然没有经q重写的 c,在承了objectcȝequals()和hashcode()Ҏ后,也会遵守q个原则?
4.谈到hashcode()和equals()׃能不说到hashset,hashmap,hashtable中的使用Q具体是怎样呢,L如下分析Q?
Hashset是承Set接口QSet接口又实现Collection接口Q这是层ơ关pR那么hashset是根据什么原理来存取对象的呢Q?
在hashset中不允许出现重复对象Q元素的位置也是不确定的。在hashset中又是怎样判定元素是否重复的呢Q这是问题的关键所在,l过一下午的查询求证终于获得了一点启C,和大家分享一下,在java的集合中Q判断两个对象是否相{的规则是:
1)Q判断两个对象的hashCode是否相等
如果不相{,认ؓ两个对象也不相等Q完?
如果相等Q{?)
Q这一点只是ؓ了提高存储效率而要求的Q其实理Z没有也可以,但如果没有,实际使用时效率会大大降低Q所以我们这里将其做为必需的。后面会重点讲到q个问题。)
2)Q判断两个对象用equalsq算是否相等
如果不相{,认ؓ两个对象也不相等
如果相等Q认Z个对象相{(equals()是判断两个对象是否相{的关键Q?
Z么是两条准则Q难道用W一条不行吗Q不行,因ؓ前面已经说了Qhashcode()相等Ӟequals()Ҏ也可能不{,所以必ȝW?条准则进行限Ӟ才能保证加入的ؓ非重复元素?
比如下面的代码:
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(){于s2.hashcode()
System.out.println(s2.hashCode());
Set hashset=new HashSet();
hashset.add(s1);
hashset.add(s2);
/*实质上在ds1,s2Ӟq用上面说到的两点准则,可以知道hashset认ؓs1和s2是相{的Q是在添加重复元素,所以让s2覆盖了s1;*/
Iterator it=hashset.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
最后在while循环的时候只打印Z一?#8221;zhaoxudong”?
输出l果为:false
true
-967303459
-967303459
q是因ؓStringcdl重写了equals()Ҏ和hashcode()ҎQ所以在Ҏ上面的第1.2条原则判定时Qhashset认ؓ它们是相{的对象Q进行了重复d?
但是看下面的E序Q?
import java.util.*;
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;
}
}
输出l果为:
1:zhangsan
1:zhangsan
3:wangwu
2:lisi
问题出现了,Z么hashsetd了相{的元素呢,q是不是和hashset的原则违背了呢?回答是:没有
因ؓ在根据hashcode()对两ơ徏立的new Student(1,"zhangsan")对象q行比较Ӟ生成的是不同的哈希码|所以hashset把他当作不同的对象对待了Q当然此时的 equals()Ҏq回的g不等Q这个不用解释了吧)。那么ؓ什么会生成不同的哈希码值呢Q上面我们在比较s1和s2的时候不是生成了同样的哈希码 吗?原因在于我们自己写的Studentcdƈ没有重新自己的hashcode()和equals()ҎQ所以在比较Ӟ是承的objectcM?hashcode()ҎQ呵呵,各位q记得objectcM的hashcode()Ҏ比较的是什么吧Q!
它是一个本地方法,比较的是对象的地址Q引用地址Q,使用newҎ创徏对象Q两ơ生成的当然是不同的对象了(q个大家都能理解吧。。。)Q造成 的结果就是两个对象的hashcode()q回的g一栗所以根据第一个准则,hashset会把它们当作不同的对象对待,自然也用不着W二个准则进?判定了。那么怎么解决q个问题呢?Q?
{案是:在StudentcM重新hashcode()和equals()Ҏ?
例如Q?
class Student
{
int num;
String name;
Student(int num,String name)
{
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 && name.equals(s.name);
}
public String toString()
{
return num+":"+name;
}
}
Ҏ重写的方法,即便两次调用了new Student(1,"zhangsan")Q我们在获得对象的哈希码ӞҎ重写的方法hashcode()Q获得的哈希码肯定是一LQ这一点应该没有疑问吧Q?
当然Ҏequals()Ҏ我们也可判断是相同的。所以在向hashset集合中添加时把它们当作重复元素看待了。所以运行修改后的程序时Q我们会发现q行l果是:
1:zhangsan
3:wangwu
2:lisi
可以看到重复元素的问题已l消除?
关于在hibernate的pojocMQ重新equals()和hashcode()的问题:
1)Q重ҎequalsQ重写hashCode只是技术要求(Z提高效率Q?
2)Qؓ什么要重写equals呢,因ؓ在java的集合框架中Q是通过equals来判断两个对象是否相{的
3)Q在hibernate中,l常使用set集合来保存相兛_象,而set集合是不允许重复的。我们再来谈谈前面提到在向hashset集合中添加元素时,怎样判断对象是否相同的准则,前面说了两条Q其实只要重写equals()q一条也可以?
但当hashset中元素比较多Ӟ或者是重写的equals()Ҏ比较复杂Ӟ我们只用equals()Ҏq行比较判断Q效率也会非怽Q?所以引入了hashcode()q个ҎQ只是ؓ了提高效率,但是我觉得这是非常有必要的(所以我们在前面以两条准则来q行hashset的元素是否重?的判断)?
比如可以q样写:
public int hashCode(){
return 1;}//{h于hashcode无效
q样做的效果是在比较哈希码的时候不能进行判断,因ؓ每个对象q回的哈希码都是1Q每ơ都必须要经q比较equals()Ҏ后才能进行判断是否重复,q当然会引v效率的大大降低?
我有一个问题,如果像前面提到的在hashset中判断元素是否重复的必要Ҏ是equals()ҎQ根据网上找到的观点Q,但是q里q没有涉及到关于哈希表的问题Q可是这个集合却叫hashsetQ这是ؓ什么?Q?
我想Q在hashmap,hashtable中的存储操作Q依焉守上面的准则。所以这里不再多说。这些是今天看书Q网上查询资料,自己ȝ出来 的,部分代码和语a是引qͼ但是千真万确是自己ȝ出来的。有错误之处和不详细不清楚的地方q请大家指出Q我也是初学者,所以难免会有错误的地方Q希望大 家共同讨论?

]]>