Set集合
就像把對象隨意扔進罐子里,無法記住元素的添加順序。Set某種程度就是Collection,方法沒有不同,只是行為稍微不同,(不允許重復元素),如果一定要往里加兩個相同元素,添加失敗add()返回false;
上面的Set的一些共同點,Hashset,TreeSet,EunmSet三個實現類還各有特色。
依次介紹下
Hashset
判斷Hashset 集合里的兩個對象相等,過兩關,equal()比較相等,對象的hashcode()也相等
為什么還得比較對象的hashcode()?
Hashset 集合收進一個對象時,會調用對象的hashcode()得到其Hashcode值來決定他的存儲位置。所以,即使是equal()比較相等的兩個對象,hashcode不同,存放在hashset里的位置不同,依然能把這兩個對象添加成功。
注意:把對象裝進hashset時,如果要重寫equals方法,也得重寫hashcode 方法,因為equals()相等的兩對象hashcode 也是相同的。
提問:hashcode()對hashset是很重要的嗎?
答:hash算法是快速查找被檢索的對象。通過對象的hashcode定位集合里的對象的存儲位置。定位該元素。對比下,數組是存儲一組元素最快的數組結構,數組通過索引找到它的組員,通過索引能計算元素在內存里的存儲位置。
但是為嘛有了數組,還用hashset呢?數組也有局限性,索引是連續的,而且長度不可變。
hashset有了hashcode,所以能快速定位對象位置,而且任意增加對象。
重寫hashcode() 注意java.lang.Object中對hashCode的約定:
兩個對象通過equals()比較相等時,他們的hashcode 也應該是一樣的。
程序運行過程中,同一個對象多次調用hashcode方法返回應該是一樣的。
如果根據 equals(java.lang.Object) 方法,兩個對象不相等,那么在兩個對象中的任一對象上調用 hashCode 方法不一定會生成不同的整數結果。但是,為不相等的對象生成不同整數結果可以提高哈希表的性能。 實際上,由 Object 類定義的 hashCode 方法確實會針對不同的對象返回不同的整數。
向hashset里添加了一個可變對象后時,要注意:如果后面的程序修改了這個可變對象的實例變量時,可能會導致他與集合里的其他元素相同,即兩個對象equals返回true,hashcode也相同。導致hashSet不能正確操作那些元素。
補充了解下,可變對象:創建后,對象的屬性值可能會變,也就是說,創建后對象的hash值可能會改變。
舉例:對象MutableKey的鍵在創建時變量 i=10 j=20,哈希值是1291。然后我們改變實例的變量值,該對象的鍵 i 和 j 從10和20分別改變成30和40。現在Key的哈希值已經變成1931。顯然,這個對象的鍵在創建后發生了改變。所以類MutableKey是可變的。
下面代碼是hashset里添加了一個可變對象例子,
可看出,hashset已經添加了幾個成員后,修改一個成員的實例變量,會得到里面有相同的成員,因此是不對的。
但是,對最后一行,不能准確訪問成員這個。有點疑問,待解決。
package Test01;
import java.util.HashSet; import java.util.Iterator; class mutClass{ public int count; public mutClass(int count) { this.count =count; } public boolean equals(Object obj) { if(this == obj) { return true; } if(obj != null && obj.getClass() == mutClass.class) { mutClass m =(mutClass) obj; return this.count == m.count; } return false; } public int hashcode() { return this.count; } public String toString() { return "試試mutClass[count=" + count + "]"; } } public class TestHashSet { @SuppressWarnings("unchecked") public static void main(String[] args){ HashSet testHashSet =new HashSet(); mutClass a = new mutClass(3); mutClass b = new mutClass(1); mutClass c = new mutClass(-9); mutClass d = new mutClass(9); testHashSet.add(a); testHashSet.add(b); testHashSet.add(c); testHashSet.add(d); System.out.println("第一次"+testHashSet); Iterator iterator =testHashSet.iterator(); mutClass first = (mutClass) iterator.next(); first.count=9; /* testHashSet.remove(new mutClass(3)); testHashSet.remove(b); //與上一行的區別 */ System.out.println("第二次"+testHashSet); System.out.println(new mutClass(-9) == new mutClass(-9)); System.out.println("第四次"+testHashSet.contains(new mutClass(-9))); } }
hashset不能保證添加成員的順序,和自己的順序是一樣的,但是引入了一個LinkedHashSet子類,使得它能和hashset一樣,靠hashcode 找到他的存儲位置,又能維護添加成員的順序,內部靠一個鏈表實現,迭代訪問集合時有很好的性能。