一、集合框架圖
簡化圖:
說明:對於以上的框架圖有如下幾點說明
1、所有集合類都位於java.util
包下。Java的集合類主要由兩個介面派生而出:Collection和Map,Collection和Map是Java集合框架的根介面,這兩個介面又包含了一些子介面或實現類。
2、集合介面:6個介面(短虛線表示),表示不同集合型別,是集合框架的基礎。
3、抽象類:5個抽象類(長虛線表示),對集合介面的部分實現。可擴充套件為自定義集合類。
4、實現類:8個實現類(實線表示),對介面的具體實現。
5、Collection 介面是一組允許重複的物件。
6、Set 介面繼承 Collection,集合元素不重複。
7、List 介面繼承 Collection,允許重複,維護元素插入順序。
8、Map介面是鍵-值物件,與Collection介面沒有什麼關係。
9、Set、List和Map可以看做集合的三大類:
- List集合是有序集合,集合中的元素可以重複,訪問集合中的元素可以根據元素的索引來訪問。
- Set集合是無序集合,集合中的元素不可以重複,訪問集合中的元素只能根據元素本身來訪問(也是集合裡元素不允許重複的原因)。
- Map集合中儲存Key-value對形式的元素,訪問時只能根據每項元素的key來訪問其value。
二、總體分析
大致說明:
看上面的框架圖,先抓住它的主幹,即Collection和Map。
1、Collection是一個介面,是高度抽象出來的集合,它包含了集合的基本操作和屬性。Collection包含了List和Set兩大分支。
- List是一個有序的佇列,每一個元素都有它的索引。第一個元素的索引值是0。List的實現類有LinkedList, ArrayList, Vector, Stack。
- Set是一個不允許有重複元素的集合。Set的實現類有HastSet和TreeSet。HashSet依賴於HashMap,它實際上是通過HashMap實現的;TreeSet依賴於TreeMap,它實際上是通過TreeMap實現的。
2、Map是一個對映介面,即key-value鍵值對。Map中的每一個元素包含“一個key”和“key對應的value”。AbstractMap是個抽象類,它實現了Map介面中的大部分API。而HashMap,TreeMap,WeakHashMap都是繼承於AbstractMap。Hashtable雖然繼承於Dictionary,但它實現了Map介面。
3、接下來,再看Iterator。它是遍歷集合的工具,即我們通常通過Iterator迭代器來遍歷集合。我們說Collection依賴於Iterator,是因為Collection的實現類都要實現iterator()函式,返回一個Iterator物件。ListIterator是專門為遍歷List而存在的。
4、再看Enumeration,它是JDK 1.0引入的抽象類。作用和Iterator一樣,也是遍歷集合;但是Enumeration的功能要比Iterator少。在上面的框圖中,Enumeration只能在Hashtable, Vector, Stack中使用。
5、最後,看Arrays和Collections。它們是運算元組、集合的兩個工具類。
有了上面的整體框架之後,我們接下來對每個類分別進行分析。
三、Collection介面
Collection介面是處理物件集合的根介面,其中定義了很多對元素進行操作的方法。Collection介面有兩個主要的子介面List和Set,注意Map不是Collection的子介面,這個要牢記。
Collection介面中的方法如下:
![img]
其中,有幾個比較常用的方法,比如方法add()新增一個元素到集合中,addAll()將指定集合中的所有元素新增到集合中,contains()
方法檢測集合中是否包含指定的元素,toArray()方法返回一個表示集合的陣列。
另外,Collection中有一個iterator()
函式,它的作用是返回一個Iterator介面。通常,我們通過Iterator迭代器來遍歷集合。ListIterator是List介面所特有的,在List介面中,通過ListIterator()
返回一個ListIterator物件。
Collection介面有兩個常用的子介面,下面詳細介紹。
1.List介面
List集合代表一個有序集合,集合中每個元素都有其對應的順序索引。List集合允許使用重複元素,可以通過索引來訪問指定位置的集合元素。
List介面繼承於Collection介面,它可以定義一個允許重複的有序集合。因為List中的元素是有序的,所以我們可以通過使用索引(元素在List中的位置,類似於陣列下標)來訪問List中的元素,這類似於Java的陣列。
List介面為Collection直接介面。List所代表的是有序的Collection,即它用某種特定的插入順序來維護元素順序。使用者可以對列表中每個元素的插入位置進行精確地控制,同時可以根據元素的整數索引(在列表中的位置)訪問元素,並搜尋列表中的元素。實現List介面的集合主要有:ArrayList、LinkedList、Vector、Stack
。
(1)ArrayList
ArrayList是一個動態陣列,也是我們最常用的集合。它允許任何符合規則的元素插入甚至包括null。每一個ArrayList都有一個初始容量(10),該容量代表了陣列的大小。隨著容器中的元素不斷增加,容器的大小也會隨著增加。在每次向容器中增加元素的同時都會進行容量檢查,當快溢位時,就會進行擴容操作。所以如果我們明確所插入元素的多少,最好指定一個初始容量值,避免過多的進行擴容操作而浪費時間、效率。
size、isEmpty、get、set、iterator
和 listIterator 操作都以固定時間執行。add 操作以分攤的固定時間執行,也就是說,新增 n 個元素需要 O(n) 時間(由於要考慮到擴容,所以這不只是新增元素會帶來分攤固定時間開銷那樣簡單)。
ArrayList擅長於隨機訪問。同時ArrayList是非同步的。
(2)LinkedList
同樣實現List介面的LinkedList與ArrayList不同,ArrayList是一個動態陣列,而LinkedList是一個雙向連結串列。所以它除了有ArrayList的基本操作方法外還額外提供了get,remove,insert
方法在LinkedList的首部或尾部。
由於實現的方式不同,LinkedList不能隨機訪問,它所有的操作都是要按照雙重連結串列的需要執行。在列表中索引的操作將從開頭或結尾遍歷列表(從靠近指定索引的一端)。這樣做的好處就是可以通過較低的代價在List中進行插入和刪除操作。
與ArrayList一樣,LinkedList也是非同步的。如果多個執行緒同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在建立List時構造一個同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
(3)Vector
與ArrayList相似,但是Vector是同步的。所以說Vector是執行緒安全的動態陣列。它的操作與ArrayList幾乎一樣。
(4)Stack
Stack繼承自Vector,實現一個後進先出的堆疊。Stack提供5個額外的方法使得Vector得以被當作堆疊使用。基本的push和pop 方法,還有peek方法得到棧頂的元素,empty方法測試堆疊是否為空,search方法檢測一個元素在堆疊中的位置。Stack剛建立後是空棧。
2.Set介面
Set是一種不包括重複元素的Collection。它維持它自己的內部排序,所以隨機訪問沒有任何意義。與List一樣,它同樣允許null的存在但是僅有一個。由於Set介面的特殊性,所有傳入Set集合中的元素都必須不同,同時要注意任何可變物件,如果在對集合中元素進行操作時,導致e1.equals(e2)==true
,則必定會產生某些問題。Set介面有三個具體實現類,分別是雜湊集HashSet、鏈式雜湊集LinkedHashSet和樹形集TreeSet。
Set是一種不包含重複的元素的Collection,無序,即任意的兩個元素e1和e2都有e1.equals(e2)=false,Set最多有一個null元素。
需要注意的是:雖然Set中元素沒有順序,但是元素在set中的位置是由該元素的HashCode決定的,其具體位置其實是固定的。
此外需要說明一點,在set介面中的不重複是有特殊要求的。
舉一個例子:物件A和物件B,本來是不同的兩個物件,正常情況下它們是能夠放入到Set裡面的,但是如果物件A和B的都重寫了hashcode和equals方法,並且重寫後的hashcode和equals方法是相同的話。那麼A和B是不能同時放入到Set集合中去的,也就是Set集合中的去重和hashcode與equals方法直接相關。
為了更好地理解,請看下面的例子:
public class Test{
public static void main(String[] args) {
Set<String> set=new HashSet<String>();
set.add("Hello");
set.add("world");
set.add("Hello");
System.out.println("集合的尺寸為:"+set.size());
System.out.println("集合中的元素為:"+set.toString());
}
}
執行結果:
集合的尺寸為:2
集合中的元素為:[world, Hello]
分析:由於String類中重寫了hashcode和equals方法,用來比較指向的字串物件所儲存的字串是否相等。所以這裡的第二個Hello是加不進去的。
再看一個例子:
public class TestSet {
public static void main(String[] args){
Set<String> books = new HashSet<String>();
//新增一個字串物件
books.add(new String("Struts2權威指南"));
//再次新增一個字串物件,
//因為兩個字串物件通過equals方法比較相等,所以新增失敗,返回false
boolean result = books.add(new String("Struts2權威指南"));
System.out.println(result);
//下面輸出看到集合只有一個元素
System.out.println(books);
}
}
執行結果:
false
[Struts2權威指南]
說明:程式中,book集合兩次新增的字串物件明顯不是一個物件(程式通過new關鍵字來建立字串物件),當使用==運算子判斷返回false,使用equals方法比較返回true,所以不能新增到Set集合中,最後只能輸出一個元素。
(1)HashSet
HashSet 是一個沒有重複元素的集合。它是由HashMap實現的,不保證元素的順序(這裡所說的沒有順序是指:元素插入的順序與輸出的順序不一致),而且HashSet允許使用null 元素。HashSet是非同步的,如果多個執行緒同時訪問一個雜湊set,而其中至少一個執行緒修改了該set,那麼它必須保持外部同步。 HashSet按Hash演算法來儲存集合的元素,因此具有很好的存取和查詢效能。
HashSet的實現方式大致如下,通過一個HashMap儲存元素,元素是存放在HashMap的Key中,而Value統一使用一個Object物件。
HashSet使用和理解中容易出現的誤區:
a.HashSet中存放null值。HashSet中是允許存入null值的,但是在HashSet中僅僅能夠存入一個null值。
b.HashSet中儲存元素的位置是固定的。HashSet中儲存的元素的是無序的,這個沒什麼好說的,但是由於HashSet底層是基於Hash演算法實現的,使用了hashcode,所以HashSet中相應的元素的位置是固定的。
c.必須小心操作可變物件(Mutable Object
)。如果一個Set中的可變元素改變了自身狀態導致Object.equals(Object)=true
將導致一些問題。
(2)LinkedHashSet
LinkedHashSet繼承自HashSet,其底層是基於LinkedHashMap來實現的,有序,非同步。LinkedHashSet集合同樣是根據元素的hashCode值來決定元素的儲存位置,但是它同時使用連結串列維護元素的次序。這樣使得元素看起來像是以插入順序儲存的,也就是說,當遍歷該集合時候,LinkedHashSet將會以元素的新增順序訪問集合的元素。
(3)TreeSet
TreeSet是一個有序集合,其底層是基於TreeMap實現的,非執行緒安全。TreeSet可以確保集合元素處於排序狀態。TreeSet支援兩種排序方式,自然排序和定製排序,其中自然排序為預設的排序方式。當我們構造TreeSet時,若使用不帶引數的建構函式,則TreeSet的使用自然比較器;若使用者需要使用自定義的比較器,則需要使用帶比較器的引數。
注意:TreeSet集合不是通過hashcode和equals函式來比較元素的.它是通過compare或者comparaeTo函式來判斷元素是否相等.compare函式通過判斷兩個物件的id,相同的id判斷為重複元素,不會被加入到集合中。
四、Map介面
Map與List、Set介面不同,它是由一系列鍵值對組成的集合,提供了key到Value的對映。同時它也沒有繼承Collection。在Map中它保證了key與value之間的一一對應關係。也就是說一個key對應一個value,所以它不能存在相同的key值,當然value值可以相同。
1.HashMap
以雜湊表資料結構實現,查詢物件時通過雜湊函式計算其位置,它是為快速查詢而設計的,其內部定義了一個hash表陣列(Entry[] table),元素會通過雜湊轉換函式將元素的雜湊地址轉換成陣列中存放的索引,如果有衝突,則使用雜湊連結串列的形式將所有相同雜湊地址的元素串起來,可能通過檢視HashMap.Entry的原始碼它是一個單連結串列結構。
2.LinkedHashMap
LinkedHashMap是HashMap的一個子類,它保留插入的順序,如果需要輸出的順序和輸入時的相同,那麼就選用LinkedHashMap。
LinkedHashMap是Map介面的雜湊表和連結列表實現,具有可預知的迭代順序。此實現提供所有可選的對映操作,並允許使用null值和null鍵。此類不保證對映的順序,特別是它不保證該順序恆久不變。
LinkedHashMap實現與HashMap的不同之處在於,後者維護著一個執行於所有條目的雙重連結列表。此連結列表定義了迭代順序,該迭代順序可以是插入順序或者是訪問順序。
根據連結串列中元素的順序可以分為:按插入順序的連結串列,和按訪問順序(呼叫get方法)的連結串列。預設是按插入順序排序,如果指定按訪問順序排序,那麼呼叫get方法後,會將這次訪問的元素移至連結串列尾部,不斷訪問可以形成按訪問順序排序的連結串列。
注意,此實現不是同步的。如果多個執行緒同時訪問連結的雜湊對映,而其中至少一個執行緒從結構上修改了該對映,則它必須保持外部同步。由於LinkedHashMap需要維護元素的插入順序,因此效能略低於HashMap的效能,但在迭代訪問Map裡的全部元素時將有很好的效能,因為它以連結串列來維護內部順序。
3.TreeMap
TreeMap 是一個有序的key-value集合,非同步,基於紅黑樹(Red-Black tree)實現,每一個key-value節點作為紅黑樹的一個節點。TreeMap儲存時會進行排序的,會根據key來對key-value鍵值對進行排序,其中排序方式也是分為兩種,一種是自然排序,一種是定製排序,具體取決於使用的構造方法。
自然排序:TreeMap中所有的key必須實現Comparable介面,並且所有的key都應該是同一個類的物件,否則會報ClassCastException異常。
定製排序:定義TreeMap時,建立一個comparator物件,該物件對所有的treeMap中所有的key值進行排序,採用定製排序的時候不需要TreeMap中所有的key必須實現Comparable介面。
TreeMap判斷兩個元素相等的標準:兩個key通過compareTo()
方法返回0,則認為這兩個key相等。
如果使用自定義的類來作為TreeMap中的key值,且想讓TreeMap能夠良好的工作,則必須重寫自定義類中的equals()
方法,TreeMap中判斷相等的標準是:兩個key通過equals()
方法返回為true,並且通過compareTo()
方法比較應該返回為0。
五、Iterator 與 ListIterator詳解
1.Iterator
Iterator的定義如下:
public interface Iterator<E> {}
Iterator是一個介面,它是集合的迭代器。集合可以通過Iterator去遍歷集合中的元素。
Iterator提供的API介面如下:
- boolean hasNext():判斷集合裡是否存在下一個元素。如果有,hasNext()方法返回 true。
- Object next():返回集合裡下一個元素。
- void remove():刪除集合裡上一次next方法返回的元素。
使用示例:
public class IteratorExample {
public static void main(String[] args) {
ArrayList<String> a = new ArrayList<String>();
a.add("aaa");
a.add("bbb");
a.add("ccc");
System.out.println("Before iterate : " + a);
Iterator<String> it = a.iterator();
while (it.hasNext()) {
String t = it.next();
if ("bbb".equals(t)) {
it.remove();
}
}
System.out.println("After iterate : " + a);
}
}
輸出結果如下:
Before iterate : [aaa, bbb, ccc]
After iterate : [aaa, ccc]
注意:
- Iterator只能單向移動。
- Iterator.remove()是唯一安全的方式來在迭代過程中修改集合;如果在迭代過程中以任何其它的方式修改了基本集合將會產生未知的行為。而且每呼叫一次
next()
方法,remove()
方法只能被呼叫一次,如果違反這個規則將丟擲一個異常。
2.ListIterator
ListIterator是一個功能更加強大的迭代器, 它繼承於Iterator介面,只能用於各種List型別的訪問。可以通過呼叫listIterator()
方法產生一個指向List開始處的ListIterator, 還可以呼叫listIterator(n)
方法建立一個一開始就指向列表索引為n的元素處的ListIterator。
ListIterator介面定義如下:
public interface ListIterator<E> extends Iterator<E> {
boolean hasNext();
E next();
boolean hasPrevious();
E previous();
int nextIndex();
int previousIndex();
void remove();
void set(E e);
void add(E e);
}
由以上定義我們可以推出ListIterator可以:
- 雙向移動(向前/向後遍歷).
- 產生相對於迭代器在列表中指向的當前位置的前一個和後一個元素的索引.
- 可以使用
set()
方法替換它訪問過的最後一個元素. - 可以使用
add()
方法在next()
方法返回的元素之前或previous()
方法返回的元素之後插入一個元素.
使用示例:
public class ListIteratorExample {
public static void main(String[] args) {
ArrayList<String> a = new ArrayList<String>();
a.add("aaa");
a.add("bbb");
a.add("ccc");
System.out.println("Before iterate : " + a);
ListIterator<String> it = a.listIterator();
while (it.hasNext()) {
System.out.println(it.next() + ", " + it.previousIndex() + ", " + it.nextIndex());
}
while (it.hasPrevious()) {
System.out.print(it.previous() + " ");
}
System.out.println();
it = a.listIterator(1);
while (it.hasNext()) {
String t = it.next();
System.out.println(t);
if ("ccc".equals(t)) {
it.set("nnn");
} else {
it.add("kkk");
}
}
System.out.println("After iterate : " + a);
}
}
輸出結果如下:
Before iterate : [aaa, bbb, ccc]
aaa, 0, 1
bbb, 1, 2
ccc, 2, 3
ccc bbb aaa
bbb
ccc
After iterate : [aaa, bbb, kkk, nnn]
六、異同點
1.ArrayList和LinkedList
- ArrayList是實現了基於動態陣列的資料結構,LinkedList基於連結串列的資料結構。
- 對於隨機訪問get和set,ArrayList絕對優於LinkedList,因為LinkedList要移動指標。
- 對於新增和刪除操作add和remove,LinedList比較佔優勢,因為ArrayList要移動資料。
這一點要看實際情況的。若只對單條資料插入或刪除,ArrayList的速度反而優於LinkedList。但若是批量隨機的插入刪除資料,LinkedList的速度大大優於ArrayList. 因為ArrayList每插入一條資料,要移動插入點及之後的所有資料。
2.HashTable與HashMap
相同點:
- 都實現了
Map、Cloneable、java.io.Serializable
介面。 - 都是儲存"鍵值對(key-value)"的雜湊表,而且都是採用拉鍊法實現的。
不同點:
(1)歷史原因:HashTable是基於陳舊的Dictionary類的,HashMap是Java 1.2引進的Map介面的一個實現 。
(2)同步性:HashTable是執行緒安全的,也就是說是同步的,而HashMap是執行緒序不安全的,不是同步的 。
(3)對null值的處理:HashMap的key、value都可為null,HashTable的key、value都不可為null 。
(4)基類不同:HashMap繼承於AbstractMap,而Hashtable繼承於Dictionary。
- Dictionary是一個抽象類,它直接繼承於Object類,沒有實現任何介面。Dictionary類是JDK 1.0的引入的。雖然Dictionary也支援“新增key-value鍵值對”、“獲取value”、“獲取大小”等基本操作,但它的API函式比Map少;而且Dictionary一般是通過Enumeration(列舉類)去遍歷,Map則是通過Iterator(迭代M器)去遍歷。然而由於Hashtable也實現了Map介面,所以,它即支援Enumeration遍歷,也支援Iterator遍歷。
- AbstractMap是一個抽象類,它實現了Map介面的絕大部分API函式;為Map的具體實現類提供了極大的便利。它是JDK 1.2新增的類。
(5)支援的遍歷種類不同:HashMap只支援Iterator(迭代器)遍歷。而Hashtable支援Iterator(迭代器)和Enumeration(列舉器)兩種方式遍歷。
3.HashMap、Hashtable、LinkedHashMap和TreeMap比較
Hashmap 是一個最常用的Map,它根據鍵的HashCode 值儲存資料,根據鍵可以直接獲取它的值,具有很快的訪問速度。遍歷時,取得資料的順序是完全隨機的。HashMap最多隻允許一條記錄的鍵為Null;允許多條記錄的值為Null;HashMap不支援執行緒的同步,即任一時刻可以有多個執行緒同時寫HashMap;可能會導致資料的不一致。如果需要同步,可以用Collections的synchronizedMap方法使HashMap具有同步的能力。
Hashtable 與 HashMap類似,不同的是:它不允許記錄的鍵或者值為空;它支援執行緒的同步,即任一時刻只有一個執行緒能寫Hashtable,因此也導致了Hashtale在寫入時會比較慢。
LinkedHashMap儲存了記錄的插入順序,在用Iterator遍歷LinkedHashMap時,先得到的記錄肯定是先插入的,也可以在構造時用帶引數,按照應用次數排序。在遍歷的時候會比HashMap慢,不過有種情況例外,當HashMap容量很大,實際資料較少時,遍歷起來可能會比LinkedHashMap慢,因為LinkedHashMap的遍歷速度只和實際資料有關,和容量無關,而HashMap的遍歷速度和他的容量有關。
如果需要輸出的順序和輸入的相同,那麼用LinkedHashMap可以實現,它還可以按讀取順序來排列,像連線池中可以應用。LinkedHashMap實現與HashMap的不同之處在於,後者維護著一個執行於所有條目的雙重連結串列。此連結列表定義了迭代順序,該迭代順序可以是插入順序或者是訪問順序。對於LinkedHashMap而言,它繼承與HashMap、底層使用雜湊表與雙向連結串列來儲存所有元素。其基本操作與父類HashMap相似,它通過重寫父類相關的方法,來實現自己的連結列表特性。
TreeMap實現SortMap介面,內部實現是紅黑樹。能夠把它儲存的記錄根據鍵排序,預設是按鍵值的升序排序,也可以指定排序的比較器,當用Iterator 遍歷TreeMap時,得到的記錄是排過序的。TreeMap不允許key的值為null。非同步的。
一般情況下,我們用的最多的是HashMap,HashMap裡面存入的鍵值對在取出的時候是隨機的,它根據鍵的HashCode值儲存資料,根據鍵可以直接獲取它的值,具有很快的訪問速度。在Map 中插入、刪除和定位元素,HashMap 是最好的選擇。
TreeMap取出來的是排序後的鍵值對。但如果您要按自然順序或自定義順序遍歷鍵,那麼TreeMap會更好。
LinkedHashMap 是HashMap的一個子類,如果需要輸出的順序和輸入的相同,那麼用LinkedHashMap可以實現,它還可以按讀取順序來排列,像連線池中可以應用。
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.TreeMap;
public class MapTest {
public static void main(String[] args) {
//HashMap
HashMap<String,String> hashMap = new HashMap();
hashMap.put("4", "d");
hashMap.put("3", "c");
hashMap.put("2", "b");
hashMap.put("1", "a");
Iterator<String> iteratorHashMap = hashMap.keySet().iterator();
System.out.println("HashMap-->");
while (iteratorHashMap.hasNext()){
Object key1 = iteratorHashMap.next();
System.out.println(key1 + "--" + hashMap.get(key1));
}
//LinkedHashMap
LinkedHashMap<String,String> linkedHashMap = new LinkedHashMap();
linkedHashMap.put("4", "d");
linkedHashMap.put("3", "c");
linkedHashMap.put("2", "b");
linkedHashMap.put("1", "a");
Iterator<String> iteratorLinkedHashMap = linkedHashMap.keySet().iterator();
System.out.println("LinkedHashMap-->");
while (iteratorLinkedHashMap.hasNext()){
Object key2 = iteratorLinkedHashMap.next();
System.out.println(key2 + "--" + linkedHashMap.get(key2));
}
//TreeMap
TreeMap<String,String> treeMap = new TreeMap();
treeMap.put("4", "d");
treeMap.put("3", "c");
treeMap.put("2", "b");
treeMap.put("1", "a");
Iterator<String> iteratorTreeMap = treeMap.keySet().iterator();
System.out.println("TreeMap-->");
while (iteratorTreeMap.hasNext()){
Object key3 = iteratorTreeMap.next();
System.out.println(key3 + "--" + treeMap.get(key3));
}
}
}
輸出結果:
HashMap-->
3--c
2--b
1--a
4--d
LinkedHashMap-->
4--d
3--c
2--b
1--a
TreeMap-->
1--a
2--b
3--c
4--d
4.HashSet、LinkedHashSet、TreeSet比較
Set介面
Set不允許包含相同的元素,如果試圖把兩個相同元素加入同一個集合中,add方法返回false。
Set判斷兩個物件相同不是使用==運算子,而是根據equals方法。也就是說,只要兩個物件用equals方法比較返回true,Set就不會接受這兩個物件。
HashSet
HashSet有以下特點:
- 不能保證元素的排列順序,順序有可能發生變化。
- 不是同步的。
- 集合元素可以是null,但只能放入一個null。
當向HashSet結合中存入一個元素時,HashSet會呼叫該物件的hashCode()方法來得到該物件的hashCode值,然後根據 hashCode值來決定該物件在HashSet中儲存位置。簡單的說,HashSet集合判斷兩個元素相等的標準是兩個物件通過equals方法比較相等,並且兩個物件的hashCode()方法返回值也相等。
注意,如果要把一個物件放入HashSet中,重寫該物件對應類的equals方法,也應該重寫其hashCode()方法。其規則是如果兩個物件通過equals方法比較返回true時,其hashCode也應該相同。另外,物件中用作equals比較標準的屬性,都應該用來計算 hashCode的值。
LinkedHashSet
LinkedHashSet集合同樣是根據元素的hashCode值來決定元素的儲存位置,但是它同時使用連結串列維護元素的次序。這樣使得元素看起來像是以插入順序儲存的,也就是說,當遍歷該集合時候,LinkedHashSet將會以元素的新增順序訪問集合的元素。
LinkedHashSet在迭代訪問Set中的全部元素時,效能比HashSet好,但是插入時效能稍微遜色於HashSet。
TreeSet類
TreeSet是SortedSet介面的唯一實現類,TreeSet可以確保集合元素處於排序狀態。TreeSet支援兩種排序方式,自然排序和定製排序,其中自然排序為預設的排序方式。向TreeSet中加入的應該是同一個類的物件。
TreeSet判斷兩個物件不相等的方式是兩個物件通過equals方法返回false,或者通過CompareTo方法比較沒有返回0。
自然排序
自然排序使用要排序元素的CompareTo(Object obj)
方法來比較元素之間大小關係,然後將元素按照升序排列。
Java提供了一個Comparable介面,該介面裡定義了一個
compareTo(Object obj)
方法,該方法返回一個整數值,實現了該介面的物件就可以比較大小。obj1.compareTo(obj2)
方法如果返回0,則說明被比較的兩個物件相等,如果返回一個正數,則表明obj1大於obj2,如果是負數,則表明obj1小於obj2。如果我們將兩個物件的equals方法總是返回true,則這兩個物件的compareTo方法返回應該返回0。
定製排序
自然排序是根據集合元素的大小,以升序排列,如果要定製排序,應該使用Comparator介面,實現 int compare(T o1,T o2)
方法。
package com.test;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.TreeSet;
/**
* @description 幾個set的比較
* HashSet:雜湊表是通過使用稱為雜湊法的機制來儲存資訊的,元素並沒有以某種特定順序來存放;
* LinkedHashSet:以元素插入的順序來維護集合的連結表,允許以插入的順序在集合中迭代;
* TreeSet:提供一個使用樹結構儲存Set介面的實現,物件以升序順序儲存,訪問和遍歷的時間很快。
* @author Zhou-Jingxian
*
*/
public class SetDemo {
public static void main(String[] args) {
HashSet<String> hs = new HashSet<String>();
hs.add("B");
hs.add("A");
hs.add("D");
hs.add("E");
hs.add("C");
hs.add("F");
System.out.println("HashSet 順序:\n"+hs);
LinkedHashSet<String> lhs = new LinkedHashSet<String>();
lhs.add("B");
lhs.add("A");
lhs.add("D");
lhs.add("E");
lhs.add("C");
lhs.add("F");
System.out.println("LinkedHashSet 順序:\n"+lhs);
TreeSet<String> ts = new TreeSet<String>();
ts.add("B");
ts.add("A");
ts.add("D");
ts.add("E");
ts.add("C");
ts.add("F");
System.out.println("TreeSet 順序:\n"+ts);
}
}
輸出結果:
HashSet 順序:[D, E, F, A, B, C]
LinkedHashSet 順序:[B, A, D, E, C, F]
TreeSet 順序:[A, B, C, D, E, F]
5、Iterator和ListIterator區別
我們在使用List,Set的時候,為了實現對其資料的遍歷,我們經常使用到了Iterator(迭代器)。使用迭代器,你不需要干涉其遍歷的過程,只需要每次取出一個你想要的資料進行處理就可以了。但是在使用的時候也是有不同的。
List和Set都有iterator()
來取得其迭代器。對List來說,你也可以通過listIterator()取得其迭代器,兩種迭代器在有些時候是不能通用的,Iterator和ListIterator主要區別在以下方面:
- ListIterator有
add()
方法,可以向List中新增物件,而Iterator不能 - ListIterator和Iterator都有
hasNext()
和next()
方法,可以實現順序向後遍歷,但是ListIterator有hasPrevious()
和previous()
方法,可以實現逆向(順序向前)遍歷。Iterator就不可以。 - ListIterator可以定位當前的索引位置,
nextIndex()
和previousIndex()
可以實現。Iterator沒有此功能。 - 都可實現刪除物件,但是ListIterator可以實現物件的修改,
set()
方法可以實現。Iierator僅能遍歷,不能修改。
因為ListIterator的這些功能,可以實現對LinkedList等List資料結構的操作。其實,陣列物件也可以用迭代器來實現。
6、Collection 和 Collections區別
(1)java.util.Collection
是一個集合介面(集合類的一個頂級介面)。它提供了對集合物件進行基本操作的通用介面方法。Collection介面在Java 類庫中有很多具體的實現。Collection介面的意義是為各種具體的集合提供了最大化的統一操作方式,其直接繼承介面有List與Set。
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
(2)java.util.Collections 是一個包裝類(工具類/幫助類)。它包含有各種有關集合操作的靜態多型方法。此類不能例項化,就像一個工具類,用於對集合中元素進行排序、搜尋以及執行緒安全等各種操作,服務於Java的Collection框架。
程式碼示例:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestCollections {
public static void main(String args[]) {
//注意List是實現Collection介面的
List list = new ArrayList();
double array[] = { 112, 111, 23, 456, 231 };
for (int i = 0; i < array.length; i++) {
list.add(new Double(array[i]));
}
Collections.sort(list);
for (int i = 0; i < array.length; i++) {
System.out.println(list.get(i));
}
// 結果:23.0 111.0 112.0 231.0 456.0
}
}