天天看點

HashSet介紹

原文位址:http://hxraid.iteye.com/blog/448884/

(1) 為啥要用HahSet?

      假如我們現在想要在一大堆資料中查找X資料。LinkedList的資料結構就不說了,查找效率低的可怕。ArrayList哪,如果我們不知道X的位置序号,還是一樣要全部周遊一次直到查到結果,效率一樣可怕。HashSet天生就是為了提高查找效率的。

(2) hashCode 散列碼

      散列碼是由對象導出的一個整數值。在Object中有一個hashCode方法來得到散列碼。基本上,每一個對象都有一個預設的散列碼,其值就是對象的記憶體位址。但也有一些對象的散列碼不同,比如String對象,它的散列碼是對内容的計算結果:

Java代碼  

HashSet介紹
  1. //String對象的散列碼計算  
  2. String str="hello";  
  3. int hash=0;  
  4. for(int i=0;i<length();i++)  
  5.     hash=31*hash+charAt(i);  

      那麼下面散列碼的結果不同也就好解釋了。s和t都還是String對象,散列碼由内容獲得,結果一樣。sb和tb是StringBuffer對象,自身沒有hashCode方法,隻能繼承Object的預設方法,散列碼是對象位址,當然不一樣了。

Java代碼  

HashSet介紹
  1. String s=new String("OK");//散列碼: 3030  
  2. String t="Ok";  /散列碼: 3030  
  3. StringBuffer sb=new StringBuffer(s);  //散列碼:20526976  
  4. StringBuffer tb=new StringBuffer(t);  //散列碼:20527144  

(3)  HashSet 散清單的内部結構

      HashSet是個連結清單數組。每一個數組元素就是一個清單,我們稱為散清單元 。

HashSet介紹

(4) HashSet 如何add機制

        假如我們有一個資料(散列碼76268),而此時的HashSet有128個散列單元,那麼這個資料将有可能插入到數組的第108個連結清單中(76268%128=108)。但這隻是有可能,如果在第108号連結清單中發現有一個老資料與新資料equals()=true的話,這個新資料将被視為已經加入,而不再重複丢傳入連結表。

       那麼資料的散列碼我知道,但HashSet的散列單元大小如何指定那?

       Java預設的散列單元大小全部都是2的幂,初始值為16(2的4次幂)。假如16條連結清單中的75%連結有資料的時候,則認為加載因子達到預設的0.75。HahSet開始重新散列,也就是将原來的散列結構全部抛棄,重新開辟一個散列單元大小為32(2的5次幂)的散列結果,并重新計算各個資料的存儲位置。以此類推下去.....

(5) 為什麼HashSet查找效率提高了。

      知道了HashSet的add機制後,查找的道理一樣。直接根據資料的散列碼和散清單的數組大小計算除餘後,就得到了所在數組的位置,然後再查找連結清單中是否有這個資料即可。

      查找的代價也就是在連結清單中,但是真正一條連結清單中的資料很少,有的甚至沒有。幾乎沒有什麼疊代的代價可言了。是以散清單的查找效率建立在散列單元所指向的連結清單中的資料要少 。

(6) hashCode方法必須與equals方法必須相容

      如果我們自己定義了一個類,想對這個類的大量對象組織成散清單結構便于查找。有一點一定要注意:就是hashCode方法必須與equals方法向相容。

Java代碼  

HashSet介紹
  1. //hashCode與equals方法的相容  
  2. public class Employee{  
  3.        public int id;  
  4.        public String name="";  
  5.        //相同id對象具有相同散列碼  
  6.        public int hashCode(){   
  7.               return id;  
  8.        }  
  9.        //equals必須比較id  
  10.         public boolean equals(Employee x){  
  11.               if(this.id==x.id) return true;  
  12.               else return false;  
  13.        }  
  14. }  

         為什麼要這樣,因為HashSet不允許相同元素(equals==ture)同時存在在結構中。假如employeeX(1111,“張三”)和employee(1111,"李四"),而Employee.equals比較的是name。這樣的話,employeeX和employeeY的equals不相等。它們會根據相同的散列碼1111加入到同一個散列單元所指向的清單中。這種情況多了,連結清單的資料将很龐大,散列沖突将非常嚴重,查找效率會大幅度的降低。

(6) 總結一下

      1、HashSet不能重複存儲equals相同的資料 。原因就是equals相同,資料的散列碼也就相同(hashCode必須和equals相容)。大量相同的資料将存放在同一個散列單元所指向的連結清單中,造成嚴重的散列沖突,對查找效率是災難性的。

      2、HashSet的存儲是無序的 ,沒有前後關系,他并不是線性結構的集合。

      3、hashCode必須和equals必須相容, 這也是為了第1點。