個人理解:
Set接口是Collection接口的子類,其繼承了所有方法,HashSet集合則實現了Set接口,其內部存儲數據時依靠哈希表,一個類似數組和鏈表的結合體。設置空集合時,存在默認的容量和加載因子,再用HashSet對象調用add方法時,其實是先比較其Hash值,若是沒有的話,則直接添加到集合中,若有的話,則再equals下比較其內容(因為有可能內容不一樣,但是其Hash值一樣),若是內容不一樣,則在這個地址下添加(鏈式),若是一樣的話,則丟掉。注意就保證了其的唯一性。(以后定義變量時,都需要重寫其hashcode和equals方法)至於LinkedHashSet則在HashSet基礎上保證了其的有序性(取出和存入順序一樣)。
至此在用eclipse進行編寫java代碼時,在創建了私有屬性后,需要將下圖里get、set方法(第二行),hashCode和equals(第四行),toString(第五行),及倒數第二行的構造方法點出來(其中構造方法中包括兩種:有參和無參的,需要選擇好后點兩次)
一、Set接口:
1、Set接口介紹:
Set方法和Collection方法基本一致,通過元素的equals方法,來判斷是否為重復元素。
2、HashSet集合:
此類實現Set接口,由哈希表支持(實際上是個HashMap集合,是數組和鏈表的結合體)。HashSet集合不能保證迭代順序與元素的存儲順序相同。
3、HashSet集合存儲數據的結構:
哈希表:
哈希表底層,使用的也是數組機制數組中也存放對象,而這些對象往數組中存放時的位置比較特殊,當需要把這些對象給數組中存放時,那么會根據這些對象的特有數據結合相應的算法,計算出這個對象在數組中的位置,然后把這個對象存放在數組中。而這樣的數組就稱為哈希數組,即就是哈希表。
public HashSet()
構造一個新的空 set,其底層 HashMap 實例的默認初始容量是 16,加載因子是 0.75。
(當容量到16*0.75時,會再開16個的容量)
當向哈希表中存放元素時,需要根據元素的特有數據結合相應的算法,這個算法其實就是Object類中的hashCode方法。
public int hashCode() { int h = hash; //value是定義的字符數組 ,hash開始為0 if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
由於任何對象都是Object類的子類,所以任何對象有擁有這個方法。即就是在給哈希表中存放對象時,會調用對象的hashCode方法,算出對象在表中的存放位置,這里需要注意,如果兩個對象hashCode方法算出結果一樣,這樣現象稱為哈希沖突,這時會調用對象的equals方法,比較這兩個對象是不是同一個對象,如果equals方法返回的是true,那么就不會把第二個對象存放在哈希表中,如果返回的是false,就會把這個值存放在哈希表中。
可以理解為:
當你用HashSet對象調用add方法時,它會去你存入的值的類型的那個類里調用它的HashCode方法,計算該對象內容的hash值;
計算完成后就會去容器中找有沒有該hash值對應的值,沒有的話,則把該元素添加到容器中去。如果有的話,再調用要存入值的類型的類中的equals方法比較內容。如果內容也一樣,就丟掉這個值,不存入容器。如果內容不一樣,則存入容器。
public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true;
總結:保證HashSet集合元素的唯一,其實就是根據對象的hashCode和equals方法來決定的。如果我們往集合中存放自定義的對象,那么保證其唯一,就必須復寫hashCode和equals方法建立屬於當前對象的比較方式。
4、HashSet存儲自定義類型元素:
給HashSet中存放自定義類型元素時,需要重寫對象中的hashCode和equals方法,建立自己的比較方式,才能保證HashSet集合中的對象唯一
5、LinkedHashSet介紹:
在HashSet下面有一個子類LinkedHashSet,它是鏈表和哈希表組合的一個數據存儲結構,LinkedHashSet集合保證元素的存入和取出的順序。
二、判斷集合元素唯一的原理:
1、ArrayList的contains方法判斷元素是否重復原理:
ArrayList的contains方法會使用調用方法時,傳入的元素的equals方法依次與集合中的舊元素所比較,從而根據返回的布爾值判斷是否有重復元素。此時,當ArrayList存放自定義類型時,由於自定義類型在未重寫equals方法前,判斷是否重復的依據是地址值,所以如果想根據內容判斷是否為重復元素,需要重寫元素的equals方法。
2、HashSet的add 、contains等方法判斷元素是否重復原理:
Set集合不能存放重復元素,其添加方法在添加時會判斷是否有重復元素,有重復不添加,沒重復則添加。
HashSet集合由於是無序的,其判斷唯一的依據是元素類型的hashCode與equals方法的返回結果。規則如下:
先判斷新元素與集合內已經有的舊元素的HashCode值
①、 如果不同,說明是不同元素,添加到集合。
②、如果相同,再判斷equals比較結果。返回true則相同元素;返回false則不同元素,添加到集合。
所以,使用HashSet存儲自定義類型,如果沒有重寫該類的hashCode與equals方法,則判斷重復時,使用的是地址值,如果想通過內容比較元素是否相同,需要重寫該元素類的hashcode與equals方法。