本文主要是Java集合的概述和Set集合java
1.Java集合概述算法
1)數組能夠保存多個對象,但數組長度不可變,一旦在初始化數組時指定了數組長度,這個數組長度就是不可變的,若是須要保存數量變化的數據,數組就有點無能爲力了;並且數組沒法保存
具備映射關係的數據。爲了保存數量不肯定的數據,以及保存具備映射關係的數據,Java提供了集合類。集合類主要負責保存、盛裝其餘數據,所以集合類也被稱爲容器類。
2)Java集合類可用於存儲數量不等的多個對象,並能夠實現經常使用的數據結構,如棧、隊列等。還能夠用於保存具備映射關係的關聯數組。Java集合大體能夠分爲Set、List、Map三種體系,
其中Set表明無序、不可重複的集合;List表明有序、重複的集合;Map則表明具備映射關係的集合。Queue體系集合表明一種隊列集合實現。
3)集合類和數組不同,數組元素既能夠是基本類型的值,也能夠是對象(實際上保存的是對象的引用變量);而集合類裏只能保存對象(實際上保存的是對象的引用變量)。
4)Java集合類主要由兩個接口派生出:Collection和Map。Set和List接口是Collection接口派生的兩個子接口,他們分別表明了無序集合和有序集合;Queue是Java提供的隊列實現。
Map實現類用於保存具備映射關係的數據。Map保存的每項數據都是key-value對,也就是由key和value兩個值組成。Map裏的key是不可重複的,key用於標識集合裏的每項數據,若是須要查閱
Map中的數據時,老是根據Map的key來獲取。
5)Collection接口是List、Set和Queue接口的父接口,該接口裏定義的方法既能夠用於操做Set集合、也能夠用於操做List集合和Queue集合。
boolean add(Object o):該方法用於向集合裏添加一個元素。
boolean addAll(Collection c):該方法把集合c裏的全部元素添加到指定集合裏。
void clear():清除集合裏的全部元素,將集合長度變爲0。
boolean contains(Object o):返回集合裏是否包含指定元素。
boolean containsAll(Collection c):返回集合裏是否包含集合c裏的全部元素。
boolean isEmpty():返回集合是否爲空。當集合長度爲0時返回true,不然返回false。
Iterator iterator():返回一個Iterator對象,用於遍歷集合裏的元素。
boolean remove(Object o):刪除集合中的指定元素o,當集合中包含了一個或多個元素o時,這些元素將被刪除,該方法將返回true。
boolean removeAll(Collection c):將集合中刪除集合c裏包含的全部元素(至關於用調用該方法的集合減集合c),若是刪除了一個或一個以上的元素,則該方法返回true。
boolean retainAll(Collection c):將集合中刪除集合c裏不包含的元素(至關於把調用該方法的集合變成該集合的集合c的交集),若是該操做改變了調用該方法的集合,則該方法返回true。
int size():該方法返回集合裏元素的個數。
Object[] toArray():該方法把集合轉換成一個數組,全部的集合元素變成對應的數組元素。
eg:數組
package cn.it.lsl; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; public class CollectionTest { public static void main(String[] args) { Collection c = new ArrayList(); c.add("小明"); c.add(6); System.out.println("c集合的元素個數爲:"+c.size()); c.remove(6); System.out.println("c集合的元素個數爲:"+c.size()); System.out.println("c集合是否包含\"小明\"字符串:"+c.contains("小明")); c.add("JavaEE"); System.out.println("c集合的元素:"+c); Collection books = new HashSet(); books.add("JavaEE"); books.add("Android"); System.out.println("c集合是否徹底包含books集合?"+c.containsAll(books)); c.removeAll(books); System.out.println("c集合的元素:"+c); c.clear(); System.out.println("c集合的元素:"+c); //books集合裏只剩下c集合裏也包含的元素 books.retainAll(c); System.out.println("books集合的元素:"+books); } }
6)Iterator接口遍歷集合元素
Iterator接口也是Java集合框架的成員,主要用於遍歷Collection集合中的元素,Iterator對象也被稱爲迭代器。
Iterator接口裏定義了以下三個方法:
boolean hasNext():若是被迭代的集合元素尚未被遍歷,則返回true。
Object next():返回集合裏的下一個元素。
void remove():刪除集合裏上一次next方法返回的元素。數據結構
eg:框架
package cn.it.lsl; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; public class IteratorTest { public static void main(String[] args) { Collection books = new HashSet(); books.add("Java ee"); books.add("Java"); books.add("Andrroid"); //獲取books集合對應的迭代器 Iterator it = books.iterator(); while(it.hasNext()){ //it.next()方法返回的數據類型是Object類型 String book = (String)it.next(); System.out.println(book); if(book.equals("Java")){ it.remove(); } book = "測試字符串"; } System.out.println(books); } }
若是要建立Iterator對象,則必須有一個被迭代的集合。性能
package cn.it.lsl; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; public class IteratorTest { public static void main(String[] args) { Collection books = new HashSet(); books.add("Java ee"); books.add("Java"); books.add("Android"); //獲取books集合對應的迭代器 Iterator it = books.iterator(); while(it.hasNext()){ //it.next()方法返回的數據類型是Object類型 String book = (String)it.next(); System.out.println(book); if(book.equals("Android")){ //it.remove(); books.remove(book); } //book = "測試字符串"; } //System.out.println(books); } }
//當使用Iterator迭代訪問Collection集合元素時,Colleection集合裏的元素不能被改變,只有經過Iterator的remove方法刪除上一次next方法返回集合元素才能夠。測試
eg:this
package cn.it.lsl; import java.util.Collection; import java.util.HashSet; public class ForeachTest { public static void main(String[] args) { Collection books = new HashSet(); books.add("Java ee"); books.add("Java"); books.add("Android"); for(Object obj : books){ String book = (String)obj; System.out.println(book); if(book.equals("Android")){ //如下代碼會引起異常 //books.remove(book); } } System.out.println(books); } }
2.Set集合
Set集合與Collection基本上徹底同樣,它沒有提供任何額外的方法。實際上Set就是Collection,只是行爲略有不一樣。(Set不容許包含重複元素)。
Set集合不容許包含相同的元素,若是試圖把兩個相同的元素加入同一個Set集合中,則添加操做失敗。es5
eg:spa
package cn.it.lsl; import java.util.HashSet; import java.util.Set; public class SetTest { public static void main(String[] args) { Set books = new HashSet(); books.add(new String("java")); boolean result = books.add(new String("java")); System.out.println(result + "-->" + books); } }
1)HashSet類
(1)HashSet是Set接口的實現。HashSet按Hash算法來存儲集合中的元素,具備很好的存取和查找性能。
(2)HashSet不能保證元素的排列順序,順序可能與添加順序不一樣,順序也有可能發生變化。
(3)當向HashSet集合中存入一個元素時,HashSet會調用該對象的hashCode()方法來獲得該對象的hashCode值,而後根據該HashCode值決定該對象在HashSet中的存儲位置。若是有兩個元素
經過equals()方法比較返回true,但它們的hashCode()方法返回值不相等,HashSet將會把它們存儲在不一樣的位置,依然能夠添加成功。即,HashSet集合判斷兩個元素相等的標準是兩個
對象經過equals()方法比較相等,而且兩個對象的hashCode()方法返回值也相等。
eg:
package cn.it.lsl; import java.util.HashSet; class A{ public boolean equals(Object obj){ return true; } } class B{ public int hashCode(){ return 1; } } class C{ public int hashCode(){ return 2; } public boolean equals(Object obj){ return true; } } public class HashSetTest { public static void main(String[] args) { HashSet books = new HashSet(); books.add(new A()); books.add(new A()); books.add(new B()); books.add(new B()); books.add(new C()); books.add(new C()); System.out.println(books); } }
注意問題:當把一個對象放入HashSet中時,若是須要重寫該對象對應類的equals()方法,則也應該重寫其hashCode()方法。其規則是:若是兩個對象經過equals()方法比較返回true,則兩個對象的hashCode值也應該相同。
重寫hashCode()方法的基本規則:
1)在程序運行過程當中,同一個對象屢次調用hashCode()方法應該返回相同的值。
2)當兩個對象經過equals()方法比較返回true時,這兩個對象的hashCode()方法應返回相等的值。
3)對象中用做equals()方法比較標準的Field,都應該用來計算hashCode值。
若是向HashSet中添加一個可變對象後,後面程序修改了該可變對象的Field,則可能致使它與集合中的其餘元素相同,這就可能致使HashSet中包含兩個相同的對象。
eg:
package cn.it.lsl; import java.util.HashSet; import java.util.Iterator; class R{ int count; public R(int count){ this.count = count; } public String toString(){ return "R[count:" + count + "]"; } public boolean equals(Object obj){ if(this == obj) return true; if(obj != null && obj.getClass() == R.class){ R r = (R)obj; if(r.count == this.count){ return true; } } return false; } public int hashCode(){ return this.count; } } public class HashSetTest2 { public static void main(String[] args) { HashSet hs = new HashSet(); hs.add(new R(5)); hs.add(new R(-3)); hs.add(new R(9)); hs.add(new R(-2)); System.out.println(hs); Iterator it = hs.iterator(); R first = (R)it.next(); first.count = -3; System.out.println(hs); hs.remove(new R(-3)); System.out.println(hs); System.out.println("hs是否包含count爲-3的R對象?" + hs.contains(new R(-3))); System.out.println("hs是否包含count爲5的R對象?" + hs.contains(new R(5))); } }
當向HashSet中添加可變對象時,必須十分當心。若是修改HashSet集合中的對象,有可能致使該對象與集合中的其餘對象相等,從而致使HashSet沒法準確訪問該對象。
2)LinkedHashSet類
HashSet還有一個子類LinkedHashSet,LinkedHashSet集合也是根據元素的hashCode值來決定元素的存儲位置,但它同時使用鏈表維護元素的次序,這樣使得元素看起來是以插入的順序保存的。也就是說,當遍歷LinkedHashSet集合裏的元素時,LinkedHashSet將會按元素的添加順序來訪問集合裏的元素。
package cn.it.lsl; import java.util.LinkedHashSet; public class LinkedHashSetTest { public static void main(String[] args) { LinkedHashSet books = new LinkedHashSet(); books.add("java"); books.add("Android"); System.out.println(books); books.remove("java"); books.add("java"); System.out.println(books); } }
輸出LinkedHashSet集合的元素時,元素的順序老是與添加順序一致。
雖然LinkedHashSet使用了鏈表記錄集合元素的添加順序,但LinkedHashSet依然是HashSet,所以它依然不容許集合元素重複。
3)TreeSet類
TreeSet是SortedSet接口的實現類,能夠確保集合元素處於排序狀態。
TreeSet中的幾個方法:
Object first():返回集合中的第一個元素。
Object last():返回集合中的最後一個元素。
Object lower(Object e):返回集合中位於指定元素以前的元素(即小於指定元素的最大元素,參數元素不須要是TreeSet集合裏的元素)。
Object higher(Object e):返回集合中位於指定元素以後的元素(即大於指定元素的最小元素,參數元素不須要是TreeSet集合裏的元素)。
SortedSet subSet(formElement,toElement):返回次Set的子集合,範圍從formElement(包含)到toElement(不包含)。
SortedSet headSet(toElement):返回此Set的子集,由小於toElement的元素組成。
SortedSet tailSet(fromElement):返回此Set的子集,由大於或等於fromElement的元素組成。
package cn.it.lsl; import java.util.TreeSet; public class TreeSetTree { public static void main(String[] args) { TreeSet nums = new TreeSet(); nums.add(5); nums.add(2); nums.add(10); nums.add(-9); System.out.println(nums); System.out.println(nums.first()); System.out.println(nums.last()); System.out.println(nums.headSet(4)); //不包含4 System.out.println(nums.tailSet(5)); //包含5 System.out.println(nums.subSet(-3, 4)); } }
4)EnumSet類
EnumSet是一個專爲枚舉類設計的集合類,EnumSet中的全部元素都必須是指定枚舉類型的枚舉值。
EnumSet類沒有暴露任何構造器來建立該類的實例,程序應該經過它提供的static方法來建立EnumSet對象。
static EnumSet allOf(Class elementType):建立一個包含指定枚舉類裏全部枚舉值的EnumSet集合。
static EnumSet complementOf(EnumSet s):建立一個其元素類型與指定EnumSet裏元素類型相同的EnumSet集合,新EnumSet集合包含原EnumSet集合所不包含的、此枚舉類剩下的枚舉值(
即新EnumSet集合和原EnumSet集合的集合元素加起來就是該枚舉類的全部枚舉值)。
static EnumSet copyOf(Collection c):使用一個普通集合來建立EnumSet集合。
static EnumSet copyOf(EnumSet s):建立一個與指定EnumSet具備相同元素類型、相同集合元素的EnumSet集合。
static EnumSet noneOf(Class elementType):建立一個元素類型爲指定枚舉類型的空EnumSet。
static EnumSet of(E first, E...rest):建立一個包含一個或多個枚舉值的EnumSet集合,傳入的多個枚舉值必須屬於同一個枚舉類。
static EnumSet range(E from, E to):建立一個包含從from枚舉值到to枚舉值範圍內全部枚舉值的EnumSet集合。
eg:
package cn.it.lsl; import java.util.EnumSet; enum Season{ SPRING,SUMMER,FAIL,WINTER } public class EnumSetTest { public static void main(String[] args) { EnumSet es1 = EnumSet.allOf(Season.class); System.out.println(es1); EnumSet es2 = EnumSet.noneOf(Season.class); System.out.println(es2); es2.add(Season.WINTER); es2.add(Season.SPRING); System.out.println(es2); EnumSet es3 = EnumSet.of(Season.SUMMER , Season.WINTER); System.out.println(es3); EnumSet es4 = EnumSet.range(Season.SUMMER, Season.WINTER); System.out.println(es4); EnumSet es5 = EnumSet.complementOf(es4); System.out.println(es5); } }
複製另外一個EnumSet集合中的全部元素來建立新的EnumSet集合,或者複製另外一個Collection集合中的全部元素來建立新的EnumSet集合。當複製Collection集合中的全部元素來建立新的EnumSet集合時,要求Collection集合中的全部元素必須是同一個枚舉類的枚舉值。
package cn.it.lsl; import java.util.Collection; import java.util.EnumSet; import java.util.HashSet; public class EnumSetTest2 { public static void main(String[] args) { Collection c = new HashSet(); c.clear(); c.add(Season.FAIL); c.add(Season.SPRING); EnumSet enumSet = EnumSet.copyOf(c); System.out.println(enumSet); // c.add("java"); // c.add("Android"); // enumSet = EnumSet.copyOf(c); } }
當試圖複製一個Collection集合裏的元素來建立EnumSet集合時,必須保證Collection集合裏的全部元素都是同一個枚舉類的枚舉值。
總結:
HashSet的性能老是比TreeSet好(特別是最經常使用的添加、查詢元素等操做),由於TreeSet須要額外的紅黑樹算法來維護集合元素的次序。只有當須要一個保持排序的Set時,才應該使用TreeSet,不然都應該使用HashSet。
對於普通的插入、刪除操做,LinkedHashSet比HashSet要略微慢一點,這是由維護鏈表所帶來的額外開銷形成的;不過,由於有了鏈表,遍歷LinkedHashSet會更快。
EnumSet是全部Set實現類中性能最好的,但它只能保存同一個枚舉類的枚舉值做爲集合元素。