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

]]>