1. 程式人生 > >HashMap的工作原理-hashcode和equals的區別

HashMap的工作原理-hashcode和equals的區別

4.若兩個物件equals返回false,則hashCode不一定返回不同的int數,但為不相等的物件生成不同hashCode值可以提高雜湊表的效能。

5.若兩個物件hashCode返回相同int數,則equals不一定返回true。

6.若兩個物件hashCode返回不同int數,則equals一定返回false。

   7.同一物件在執行期間若已經儲存在集合中,則不能修改影響hashCode值的相關資訊,否則會導致記憶體洩露問題。

談到hashcode()和equals()就不能不說到hashset,hashmap,hashtable中的使用,具體是怎樣呢,請看如下分析:
Hashset是繼承Set介面,Set介面又實現
Collection介面
,這是層次關係。那麼hashset是根據什麼原理來存取物件的呢? 在hashset中不允許出現重複物件,元素的位置也是不確定的。在hashset中又是怎樣判定元素是否重複的呢?這就是問題的關鍵所在,經過一下午的查詢求證終於獲得了一點啟示,和大家分享一下,在java的集合中,判斷兩個物件是否相等的規則是: 1),判斷兩個物件的hashCode是否相等 如果不相等,認為兩個物件也不相等,完畢 如果相等,轉入2) (這一點只是為了提高儲存效率而要求的,其實理論上沒有也可以,但如果沒有,實際使用時效率會大大降低,所以我們這裡將其做為必需的。後面會重點講到這個問題。) 2),判斷兩個物件用equals運算是否相等 如果不相等,認為兩個物件也不相等 如果相等,認為兩個物件相等(equals()是判斷兩個物件是否相等的關鍵) 為什麼是兩條準則,難道用第一條不行嗎?不行,因為前面已經說了,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()等於s2.hashcode()
System.out.println(s2.hashCode());
Set hashset=new HashSet();
hashset.add(s1);
hashset.add(s2);

Iterator it=hashset.iterator();
            while(it.hasNext())
            {
             System.out.println(it.next());
            }

最後在while迴圈的時候只打印出了一個”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.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()對兩次建立的new Student(1,"zhangsan")物件進行比較時,生成的是不同的雜湊碼值,所以hashset把他當作不同的物件對待了,當然此時的equals()方法返回的值也不等(這個不用解釋了吧)。那麼為什麼會生成不同的雜湊碼值呢?上面我們在比較s1和s2的時候不是生成了同樣的雜湊碼嗎?原因就在於我們自己寫的Student類並沒有重新自己的hashcode()和equals()方法,所以在比較時,是繼承的object類中的hashcode()方法,呵呵,各位還記得object類中的hashcode()方法比較的是什麼吧!!它是一個本地方法,比較的是物件的地址(引用地址),使用new方法建立物件,兩次生成的當然是不同的物件了(這個大家都能理解吧。。。),造成的結果就是兩個物件的hashcode()返回的值不一樣。所以根據第一個準則,hashset會把它們當作不同的物件對待,自然也用不著第二個準則進行判定了。那麼怎麼解決這個問題呢??答案是:在Student類中重新hashcode()和equals()方法。例如:
 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"),我們在獲得物件的雜湊碼時,根據重寫的方法hashcode(),獲得的雜湊碼肯定是一樣的(這一點應該沒有疑問吧)。當然根據equals()方法我們也可判斷是相同的。所以在向hashset集合中新增時把它們當作重複元素看待了。所以執行修改後的程式時,我們會發現執行結果是:
1:zhangsan
3:wangwu
2:lisi
可以看到重複元素的問題已經消除。關於在hibernate的pojo類中,重新equals()和hashcode()的問題:1),重點是equals,重寫hashCode只是技術要求(為了提高效率)
2),為什麼要重寫equals呢,因為在java的集合框架中,是通過equals來判斷兩個物件是否相等的3),在hibernate中,經常使用set集合來儲存相關物件,而set集合是不允許重複的。我們再來談談前面提到在向hashset集合中新增元素時,怎樣判斷物件是否相同的準則,前面說了兩條,其實只要重寫equals()這一條也可以。但當hashset中元素比較多時,或者是重寫的equals()方法比較複雜時,我們只用equals()方法進行比較判斷,效率也會非常低,所以引入了hashcode()這個方法,只是為了提高效率,但是我覺得這是非常有必要的(所以我們在前面以兩條準則來進行hashset的元素是否重複的判斷)。比如可以這樣寫:public int hashCode(){ return 1;}//等價於hashcode無效這樣做的效果就是在比較雜湊碼的時候不能進行判斷,因為每個物件返回的雜湊碼都是1,每次都必須要經過比較equals()方法後才能進行判斷是否重複,這當然會引起效率的大大降低。我有一個問題,如果像前面提到的在hashset中判斷元素是否重複的必要方法是equals()方法(根據網上找到的觀點),但是這裡並沒有涉及到關於雜湊表的問題,可是這個集合卻叫hashset,這是為什麼??我想,在hashmap,hashtable中的儲存操作,依然遵守上面的準則。所以這裡不再多說。這些是今天看書,網上查詢資料,自己總結出來的,部分程式碼和語言是引述,但是千真萬確是自己總結出來的。有錯誤之處和不詳細不清楚的地方還請大家指出,我也是初學者,所以難免會有錯誤的地方,希望大家共同討論。