標籤:
------Java培訓、Android培訓、iOS培訓、.Net培訓、期待與您交流! -------
一、Set集合
Set集合不允許包含相同的元素,如果試圖把兩個相同的元素加入同一個Set集合中,則添加
操作失敗,add方法返回false,而新元素不會被加入。
Set判斷兩對象相同不是使用==運算子,而是根據equals方法。也就是說,只要兩個對象用
equals方法比較返回true,Ser就不會接受這兩個對象;反之,只要兩個對象用equals方法比較
返回false,SEt就會接受這兩個對象(甚至這兩個對象是同一個對象,Set也可把它們當成兩個對
象出來)。下面是使用普通Set的樣本程式。
1 import java.util.HashSet; 2 import java.util.Set; 3 4 5 public class SetTest { 6 7 public static void main(String[] args) { 8 Set names = new HashSet<>(); 9 //添加一個字串對象10 names.add(new String("暨雪"));11 //再次添加一個字串對象12 //因為兩個字串對象通過equals方法比較相等13 //所以添加失敗,返回false14 boolean reasult = names.add(new String("暨雪"));15 //從下面輸出看到集合只有一個元素16 System.out.println(reasult + "-->" + names);17 }18 19 }
運行結果:
false-->[暨雪]
從上面程式中可以看出,names集合兩次添加的字串對象明顯不是同一對象(因為兩次都調
用了new關鍵字來建立字串對象),這兩個字串對象使用==運算子判斷肯定返回false,但通過
equals方法比較將返回true,所以添加失敗。最後結果輸出只有一個元素。
1.HashSet類
HashSet是Set介面的典型實現,大多數時候使用Set集合是就是使用這個實作類別。HashSet按Hash
演算法來儲存集合中的元素,因此具有很好的存取和尋找效能。
HashSet具有以下特點:
1)不能保證元素的排序次序,順序有可能發生變化。
2)HashSet不是同步的,如果多線程同時訪問一個HashSet,假設有兩個或兩個以上線程同時
修改了HashSet集合時,則必須通過代碼來保證其同步。
3)集合元素值可以是null。
HashSet集合判斷兩個以上相等的標準是兩個對象通過equals()方法比較相等,並且兩個對象的
hashCode()方法傳回值也相等。
下面程式分別提供了三個類A、B和C,它們分別重寫了equals()、hashCode()兩個方法的一個或全部
,通過此程式可以看到HashSet判斷集合元素相同的標準。
1 import java.util.HashSet; 2 3 4 public class HashSetTest { 5 //類A的equals方法總是返回true,但沒有重寫hashCode方法 6 static class A { 7 @Override 8 public boolean equals(Object o) { 9 return true;10 }11 }12 //類B的hashCode總是返回1,但沒有重寫equals方法13 static class B {14 @Override15 public int hashCode() {16 return 1;17 }18 }19 //類C重寫了hashCode方法和equals方法20 static class C {21 @Override22 public int hashCode() {23 return 2;24 }25 @Override26 public boolean equals(Object obj) {27 return true;28 }29 }30 31 public static void main(String[] args) {32 33 HashSet names = new HashSet<>();34 names.add(new A());35 names.add(new A());36 names.add(new B());37 names.add(new B());38 names.add(new C());39 names.add(new C());40 41 System.out.println(names);42 }43 44 }
運行結果:
[[email protected], [email protected], [email protected], [email protected], [email protected]]
從上面程式可以看出,即使兩個A對象通過equals()方法比較返回true,但HashSet依然把它們當成
兩個對象;即使兩個B對象的hashCode()返回相同值(都是1),但HashSet依然把它們當成同一個
對象。
注意:
當把一個對象放入HashSet中時,如果需要重寫該方法對應類的equals()方法,則也應該重寫
hashCode()方法。其規則是:如果兩個對象通過equals()方法比較返回true,這兩個對象的hashCode
()值也應該相同。
重寫hashCode()方法的基本規則:
1)在程式運行中,同一對象多次調用hashCode()方法應該返回相同的值。
2)當兩個對象通過equals()方法比較返回true時,這兩個對象的hashCode()方法應返回相等的值。
3)對象中用作equals()方法比較標準的Filed,都應該用計算hashCode值。
hashCode傳回值的計算:
return f1.hashCode() + (int) f2;
為了避免直接相加產生偶然相等(兩個對象f1、f2Field並不相等,但他們的和恰好相等),
可以通過為個Field乘以任意一個質數後在相加。
return f1.hashCode() * 17 + (int) f2 * 13;
如果向HashSet中添加一個可變對象後,後面出現修改了該可變對象的Field,則可能導致
它與集合中的其他元素相同(即兩個對象通過equals()方法比較返回true,兩個對象的
hashCode()值也相等)。這就有可能導致HashSet中包含兩個相同的對象。下面程式示範了這
種情況。
1 import java.util.HashSet; 2 import java.util.Iterator; 3 4 class R { 5 int count; 6 public R (int count){ 7 this.count = count; 8 } 9 @Override10 public String toString() {11 12 return "R[count:" + count + "]";13 }14 @Override15 public boolean equals(Object obj) {16 17 if (this == obj) {18 return true;19 }20 if (obj != null && obj.getClass() == R.class) {21 R r = (R) obj;22 if (r.count == this.count) {23 return true;24 }25 }26 return false;27 }28 @Override29 public int hashCode() {30 31 return this.count;32 }33 }34 public class HashSetTest2 {35 36 public static void main(String[] args) {37 38 HashSet hs = new HashSet<>();39 hs.add(new R(5));40 hs.add(new R(-3));41 hs.add(new R(9));42 hs.add(new R(-2));43 //列印HashSet集合,集合沒有重複44 System.out.println(hs);45 //取出第一個元素46 Iterator it = hs.iterator();47 R first = (R) it.next();48 //為第一個元素的count執行個體變數賦值49 first.count = -3;50 //再次輸出HashSet集合,集合元素有重複元素51 System.out.println(hs);52 //刪除count為-3的R對象53 hs.remove(new R(-3));54 System.out.println(hs);55 System.out.println("hs是否包count為-3的R對象?" + hs.contains(new R(-3)));56 System.out.println("hs是否包count為5的R對象?" + hs.contains(new R(5)));57 System.out.println("hs是否包count為9的R對象?" + hs.contains(new R(9)));58 }59 60 }
運行結果:
[R[count:5], R[count:9], R[count:-3], R[count:-2]][R[count:-3], R[count:9], R[count:-3], R[count:-2]][R[count:-3], R[count:9], R[count:-2]]hs是否包count為-3的R對象?falsehs是否包count為5的R對象?falsehs是否包count為9的R對象?true
上面程式中的first.count = -3;因為改變了HashSet集合中第一個R對象的count執行個體變數
的值,這將導致該R對象與集合中的其他對象相同。當試圖刪除count = -3的R對象時,HashSet
會計算出該對象的hashCode值,從而找出該對象在集合中儲存位置,然後把此處的對象與count
為-3的R對象通過equals()方法進行比較,如果相等則刪除該對象——HashSet只有第三元素才
滿足該條件(第一個元素實際上儲存在count為5的R對象對應的位置),所以第三個元素被刪除
。至於第一個count為-3的R對象,他保持在count為5的R對象對應的位置,但是用equals()方法
拿它和count為5的R對象比較時有返回false——這將導致HashSet不可能準確訪問該元素。
Java——(三)Collection之Set集合、HashSet類