I學霸官方免費教程三十二:Java集合架構之Set集合

來源:互聯網
上載者:User

標籤:java集合架構   set集合   set介面   hashset類   treeset類   java免費教程   

Set介面

Set集合是無序的、元素不可重複的結合
常用集合類有HashSet和TreeSet

HashSet類
常用的兩種List集合各有各的優點,那麼有沒有同時具備這兩種List集合的優點的集合呢?答案是肯定的,就是Set集合。

執行個體:package collection.set.hashSet;import java.util.HashSet;import java.util.Iterator;/** * 示範HashSet * @author 學霸聯盟 - 趙燦 */public class HashSetDemo {public static void main(String[] args) {//建立HashSet對象HashSet hs = new HashSet();//迴圈向集合hs中添加元素for (int i = 0; i < 10; i++) {//建立Person對象Person p = new Person();//為Person對象的name屬性賦值//有此處可知,加入的順序是趙0、趙1、趙2......趙9p.name = "趙" + i;//將Person對象p加入集合hs.add(p);}/* * Iterator(迭代器):提供一種訪問Collection集合中每個元素的方法 * Iterator是一個介面 * 其中定義了hasNext()方法,用於判斷集合中是否含有下一個元素 * 如果還有下一個元素,返回true;反之,返回false * 還定義了next()方法,擷取下一個元素 * Collection中定義了一個iterator()方法,用於擷取Iterator對象 *  * 由於Set集合無法使用下標訪問元素,無法使用下標的方式遍曆集合元素 * 所以java為我們提供了Iterator用於訪問集合中的各個元素 */Iterator it = hs.iterator();//調用hasNext方法判斷集合中是否還有下一個元素while(it.hasNext()){//調用next方法擷取下一個元素Object obj = it.next();//強制類型轉換Person p = (Person)obj;//輸出name屬性值,和加入的順序不同System.out.print(p.name + "  ");}}}//Person類class Person{public String name;}我的機器上啟動並執行結果:趙4  趙7  趙8  趙0  趙6  趙1  趙2  趙3  趙5  趙9

      從上面的結果可以看到,輸出的順序並不是加入時的順序(未必一次就能運行出亂序的效果,可以瞬間連續多次點擊eclipse上的運行按鈕,這樣看到亂序的效果幾率比較大);所以稱Set是無序的集合。
      那麼Set是如何同時實現兩種List集合的優點,又為什麼沒有順序呢?
      首先Set同時使用了兩種List集合的結構,即數組 + 鏈表(Set中使用的是單項鏈表:一個節點中只儲存下一個節點,而不儲存上一個節點);其中數組中的每一個位置均用於儲存一個鏈表第一個元素。
      這裡不得不提到hashCode和equals。在建立Set集合的子類對象是,會建立一個Node[] table(節點數組)用於儲存Node對象,Node中儲存下一個Node和加入集合的元素。
再向集合中添加元素A時,Set會建立一個Node對象nodeA(Node nodeA = new Node()),將元素A賦值給nodeA中key屬性(nodeA.key = A),然後根據元素A的hashCode值計算出下標(假如得到的下標是1),最後將nodeA的地址賦值到下標為1的位置中。
      再次向集合中添加元素B時,又會建立Node對象nodeB,使用元素B的hashCode值計算出的下標也是1;此時會用equals方法判斷元素A和元素B這兩個對象是否相同。
      如果equals返回true,則表示A和B內容相同,此時不會再將nodeB加入集合。
      如果equals方法返回false,說明元素A和元素B是不同的兩個對象,此時會將nodeB賦值給nodeA中的next屬性(nodeA.next = nodeB),這樣就形成了單項鏈表結構。
      由以上描述可知,Set中不能添加重複的元素,而且無法從Set中快速擷取某一個指定的元素,只能使用Iterator為我們提供的遍曆集合的方法擷取Set中的每一個元素。
樣本圖
650) this.width=650;" src="http://img.blog.csdn.net/20150812161945262" />

上述中建立的table稱作散列表或雜湊表,預設初始長度為16(如果建立集合對象時自訂初始容量,自訂的容量值必須是2的整倍數),載入因子預設為0.75(當集合中添加的元素個數等於當前長度乘以載入因子時,集合會自動將容量增加至原來的2倍)。

拓展閱讀:《I學霸官方免費教程三十七:Java資料結構之單向鏈表結構》

在使用key的hashCode值計算下標(計算方法:int h = key.hashCode(); int hash = h^(h>>>16); int index = (table.length - 1) & hash),這樣可以保證不越界,但無法保證順序,所以Set集合是無序的。
這樣做帶來的優點是當加入和刪除元素時,都無需將前後元素在數組中位移,只需根據計算出的下標和equals方法得到的結果,進行加入和刪除即可。
缺點是沒有順序,無法快速擷取某個元素,必須採用遍曆的方式擷取Set集合中的元素。

另外,可以利用重寫Object類中的hashCode方法,來控制什麼樣的對象可以產生一致的(節點對象在hash表中的)下標。此時再利用重寫Object類的equals方法,來控制對象是否屬於重複的對象。而且必須兩個方法得到的結果同時滿足要求時,才認為兩個對象時同一對象;任一方法不滿足要求都認為是不同的對象。

執行個體:package collection.set.hashSet;import java.util.HashSet;import java.util.Set;/** * 示範重寫hashCode方法和equals方法 *  * @author 學霸聯盟 - 趙燦 */public class HashCodeEqualsDemo {public static void main(String[] args) {/* * 在堆記憶體中建立了一個PersonHE類型的對象 * 對象中儲存的是"張三"和"123456789"兩個字串 */PersonHE p1 = new PersonHE("張三", "123456789", 10);/* * 又在堆記憶體中建立了一個PersonHE類型的對象 * 對象中儲存的也是"張三"和"123456789"兩個字串 */PersonHE p2 = new PersonHE("張三", "123456789", 20);/* * 此時在記憶體中有兩個PersonHE類型的對象 * 如果PersonHE類沒有重寫父類Object的equals方法 * 那麼使用的則是Object類中的equals方法比較 * 和使用==比較一樣,結果是:false */System.out.println(p1 == p2);/* * 現在已經重寫了equals方法,按照姓名和***號比較 * 建立的時候姓名和***號賦的值都一樣 * 所以此時使用equals比較得到的結果是:true */System.out.println(p1.equals(p2));//建立HashSet對象(父類引用指向子類對象)Set setHE = new HashSet();//向集合中添加元素setHE.add(p1);/* * PersonHE重寫了hashCode方法和equals方法 * 使得對象p1和p2的hashCode相等,equals的結果為true * 所以不會將對象p2加入集合 */setHE.add(p2);/* * 可以使用Iterator + while迴圈擷取Set集合中的元素 * 也可以使用增強for迴圈 * 文法格式:for(資料類型  變數名 : 繼承Collection介面的集合或數組){} * 作用:迴圈一次從集合或數組中取出一個元素賦值變數 * 注意:聲明變數的資料類型,必須能夠接收集合或數組中取出的元素的類型 * 反例:String[] str = {"abc", "xyz"}; * for(int i : str){}; * 執行第一次迴圈,取出字串abc,賦值給int類型的變數i * 這很明顯是錯誤的 */for(Object obj:setHE){PersonHE pHE = (PersonHE)obj;/* * 由於加入前一個對象p1被覆蓋 * 所以這裡只會輸出一次,而且輸出的是對象p1 * 輸出結果:姓名:張三  ***號:123456789  年齡:10 */System.out.println(pHE);}}}/** * 建立人類PersonHE * @author 學霸聯盟 - 趙燦 */class PersonHE{//姓名private String name;//***號private String id;//年齡public int age;//聲明帶參構造方法public PersonHE(String name, String id, int age){this.name = name;this.id = id;this.age = age;}/** * 重寫父類的hashCode * 目的:自訂計算hash值的演算法 */@Overridepublic int hashCode() {/* * 這裡自訂的計算規則是,姓名的hash值亦或***號的hash值 * 當然演算法不是固定的,只要能符合需求即可 * 使用亦或運算只是為了儘可能少的出現相同情況 */return name.hashCode() ^ id.hashCode();}/** * 重寫父類的equals方法 * 目的:自訂比較兩個對象內容是否相同的規則 */@Overridepublic boolean equals(Object obj) {//將Object強制類型轉換為PersonPersonHE p = (PersonHE)obj;//這裡自訂的比較規則是,當姓名和***號相同時,就表示是同一個人boolean result = this.name.equals(p.name) && this.id.equals(p.id);return result;}/** * 重寫父類的toString方法 * 目的:自訂將對象轉換成字串的規則 */@Overridepublic String toString() {//這裡自訂的規則是 ,返回姓名和***號的字串return "姓名:" + name + "  ***號:" + id + "  年齡:" + age;}}運行結果:falsetrue姓名:張三  省份證號:123456789  年齡:10


總結:

Set集合是無序的,不可重複的集合,採用散列(Hash)儲存(數組 + 單向鏈表),遍曆時可以採用Iterator + 迴圈和增強for迴圈,Iterator(迭代器)主要方法:hasNext()和next(),判斷加入集合中的兩個對象是否重複:
1、判斷hashCode是否相同;
當hashCode相同,但equals比較結果為false時,兩個對象會被儲存在一個單向鏈表上,這個單向鏈表稱為“桶”
2、使用equals判斷內容是否相同;
當equals比較結果為true,但hashCode不同時,兩個對象會被儲存在不同的“桶”中


TreeSet類

既然Set集合是無序的,那麼可不可以另外給Set增加排序的方法呢?答案是肯定的。
java為我們提供了TreeSet類,實現了自動對加入Set集合的元素進行排序。但是要求加入到TreeSet集合的元素類型必須實現Comparable介面,並實現介面中的compareTo()方法,在該方法中自訂排序方式。

執行個體:package collection.set.treeSet;import java.util.TreeSet;/** * 示範泛型TreeSet集合 * @author 學霸聯盟 - 趙燦 */public class TreeSetDemo {public static void main(String[] args) {//建立三個樹枝對象Branch b1 = new Branch("樹枝10", 10);Branch b2 = new Branch("樹枝5", 5);Branch b3 = new Branch("樹枝8", 8);//建立TreeSet集合對象TreeSet ts = new TreeSet();//向集合中添加元素;加入TreeSet集合的元素類型必須實現Comparable介面ts.add(b1);ts.add(b2);ts.add(b3);//增強for迴圈遍曆集合tsfor (Object obj : ts) {//強制類型轉換Branch b = (Branch) obj;//輸出System.out.println("名稱:" + b.getName() + "  年輪" + b.getAnnualRing());}}}/** * 樹枝類Branch * 加入TreeSet集合的元素類型必須實現Comparable介面 * 否則程式啟動並執行時候會出現異常 * @author 學霸聯盟 - 趙燦 */class Branch implements Comparable{//標識名稱private String name;//年輪:用於排序的依據private int annualRing;//帶參構造方法public Branch(String name, int annualRing){this.name = name;this.annualRing = annualRing;}//重寫介面中的方法@Overridepublic int compareTo(Object o) {//強制類型轉換Branch b = (Branch)o;/* * 使用年輪作為排序比較的依據 * 當前對象年輪減去參數傳入的Branch對象的年齡 * 結果等於0表示:兩個樹枝年輪相等,排序不分先後 * 結果大於0表示:當前對象的年輪大,排在後面 * 結果小於0表示:參數對象的年輪大,排在後面 */int result = this.annualRing - b.annualRing;return result;}//擷取名稱public String getName(){return name;}//擷取年輪public int getAnnualRing(){return annualRing;}}運行結果:名稱:樹枝5  年輪5名稱:樹枝8  年輪8名稱:樹枝10  年輪10


本文出自 “學霸聯盟教育官方部落格” 部落格,轉載請與作者聯絡!

I學霸官方免費教程三十二:Java集合架構之Set集合

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.