Java基礎——集合框架
Java的集合框架是Java中很重要的一環,Java平臺提供了一個全新的集合框架。“集合框架”主要由一組用來操作物件的介面組成。不同介面描述一組不同資料型別。Java平臺的完整集合框架如下圖所示:
上述類圖中,實線邊框的是實現類,比如ArrayList,LinkedList,HashMap等,折線邊框的是抽象類,比如AbstractCollection,AbstractList,AbstractMap等,而點線邊框的是介面,比如Collection,Iterator,List等。
發現一個特點,上述所有的集合Collection類,都實現了Iterator介面,(Map中並沒有實現這個介面
- 如果是Iterator介面,那麼在遍歷集合中元素的時候,只能單向遍歷,只能通過next()訪問下一個元素,依次往後遍歷,被遍歷後的元素不會在遍歷到。所有集合均實現了這個介面,比如HashSet,HashMap;
- 而如果是ListIterator介面,那麼在遍歷集合中元素的時候,可以實現雙向遍歷,既可以通過next()訪問下一個元素,又可以通過previous()訪問前一個元素,更加靈活。但是隻有List的子類才實現了這個介面,比如ArrayList。
還有一個特點就是抽象類的使用。如果要自己實現一個集合類,去實現那些抽象的介面會非常麻煩,工作量很大。這個時候就可以使用抽象類,這些抽象類中給我們提供了許多現成的實現,我們只需要根據自己的需求重寫一些方法或者新增一些方法就可以實現自己需要的集合類,工作流昂大大降低。
將一些抽象類去掉之後的簡化版如下圖所示。
下面簡單介紹下每一種集合型別的特點:
- Collection:每個“槽”只有一個元素
- Set:每個元素必須是唯一的,不能含有重複的元素。加入Set的元素必須定義equals()方法以確保物件的唯一性。不保證維護元素的順序。
-
HashSet***:主要的特點是:存入HashSet的元素必須定義hashCode()方法
- LinkedHashSet*:主要特點是:具有HashSet的查詢速度,且背部使用連結串列維護元素的順序(插入的次序),存入LinkedHashSet的元素必須定義hashCode()方法,底層的物理結構是連結串列結合雜湊表。
- TreeSet*:主要特點是是:保持次序的Set,存入TreeSet的元素必須實現Comparable介面,在加入元素時會自動進行排序。底層的物理結構是紅黑樹。
-
HashSet***:主要的特點是:存入HashSet的元素必須定義hashCode()方法
- List:將以特定次序儲存元素,可以重複。
- ArrayList***:主要特點是:長於隨機訪問元素,但是在List中間插入和移除元素時較慢,加入元素的順序就是其存放元素的順序。底層的物理結構是陣列。
- LinkedList***:只要特點是:隨機訪問速度較慢,可以在任何位置進行高效地插入和刪除操作的有序序列。底層的物理結構是雙向連結串列。LinkedList具有直接實現Stack(棧)的所有功能的方法,因此可以直接將LinkedList作為Stack使用。LinkedList也實現很多方法支援Quene(佇列)的行為,並且其也實現了Quene介面,因此LinkedList也可以用作Quene的一種實現,可以將LinkedList向上轉型為Quene。
- Vector:主要特點是:和ArrayList功能和特點基本類似,不同的地方在於Vector是執行緒安全的,所以速度相對慢一些。底層的物理結構也是陣列。現在基本上不怎麼用了
- Stack:現在不用了,可以用LinkedList來當做Stack.
- Quene:先進先出的容器。從容器的一端放入事物,從另一端取出。一般用LinkedList來當做Quene.
- Set:每個元素必須是唯一的,不能含有重複的元素。加入Set的元素必須定義equals()方法以確保物件的唯一性。不保證維護元素的順序。
- Map:每個“槽”中存放的是鍵值對(key-value),可以使用鍵來查詢值。
-
HashMap***:主要特點是:基於散列表的實現(取代了Hashtable)。插入和查詢鍵值對的開銷是固定的。可以通過構造器設定容量和負載因子,以調整容器效能。
- LinkedHashMap*:類似於HashMap,但是迭代遍歷時,取得的鍵值對的順序是其插入的次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一點,而在迭代訪問時更快,因為其用連結串列維護內部次序,
- TreeMap**:基於紅黑樹的實現。加入TreeMap的元素會被自動排序,排序次序有Comparable或Comparator決定( Comparable和Comparator的區別)。是唯一帶有subMap()方法的Map,可以返回一個子樹。
- Hashtable:執行緒安全的,同HashMap。現在基本上不用了
- WeakedHashMap:弱鍵(weak key)對映,允許釋放對映所指向的物件
- IdentifyHashMap:使用==代替equals()對“鍵”進行比較的雜湊對映。
-
HashMap***:主要特點是:基於散列表的實現(取代了Hashtable)。插入和查詢鍵值對的開銷是固定的。可以通過構造器設定容量和負載因子,以調整容器效能。
程式碼例項:HashSetDemo
1 package edu.sjtu.erplab.collection;
2
3 import java.util.HashSet;
4 import java.util.Iterator;
5 import java.util.Set;
6
7 public class HashSetDemo {
8
9 public static void main(String[] args) {
10 Set<String> set=new HashSet<String>();
11
12 set.add("a");
13 set.add("b");
14 set.add("c");
15 set.add("c");
16 set.add("d");
17
18 //使用Iterator輸出集合
19 Iterator<String> iter=set.iterator();
20 while(iter.hasNext())
21 {
22 System.out.print(iter.next()+" ");
23 }
24 System.out.println();
25 //使用For Each輸出結合
26 for(String e:set)
27 {
28 System.out.print(e+" ");
29 }
30 System.out.println();
31
32 //使用toString輸出集合
33 System.out.println(set);
34 }
35 }
程式碼例項:TreeSet
View Code
程式碼例項:ArrayListDemo
1 package edu.sjtu.erplab.collection;
2
3 import java.util.ArrayList;
4 import java.util.Iterator;
5 import java.util.List;
6
7 public class ArrayListDemo {
8 public static void main(String[] args) {
9 List<String> arrList=new ArrayList<String>();
10
11 arrList.add("a");
12 arrList.add("b");
13 arrList.add("c");
14 arrList.add("c");
15 arrList.add("d");
16
17 //使用Iterator輸出集合
18 Iterator<String> iter=arrList.iterator();
19 while(iter.hasNext())
20 {
21 System.out.print(iter.next()+" ");
22 }
23 System.out.println();
24 //使用For Each輸出結合
25 for(String e:arrList)
26 {
27 System.out.print(e+" ");
28 }
29 System.out.println();
30
31 //使用toString輸出集合
32 System.out.println(arrList);
33 }
34 }
程式碼例項:LinkedListTest
1 package edu.sjtu.erplab.collection;
2
3 import java.util.ArrayList;
4 import java.util.Iterator;
5 import java.util.List;
6 import java.util.ListIterator;
7
8 public class LinkedListTest {
9
10 public static void main(String[] args) {
11
12 List<String> a=new ArrayList<String>();
13 a.add("a");
14 a.add("b");
15 a.add("c");
16 System.out.println(a);
17
18 List<String> b=new ArrayList<String>();
19 b.add("d");
20 b.add("e");
21 b.add("f");
22 b.add("g");
23 System.out.println(b);
24
25 //ListIterator在Iterator基礎上添加了add(),previous()和hasPrevious()方法
26 ListIterator<String> aIter=a.listIterator();
27 //普通的Iterator只有三個方法,hasNext(),next()和remove()
28 Iterator<String> bIter=b.iterator();
29
30 //b歸併入a當中,間隔交叉得插入b中的元素
31 while(bIter.hasNext())
32 {
33 if(aIter.hasNext())
34 aIter.next();
35 aIter.add(bIter.next());
36 }
37 System.out.println(a);
38
39 //在b中每隔兩個元素刪除一個
40 bIter=b.iterator();
41
42 while(bIter.hasNext())
43 {
44 bIter.next();
45 if(bIter.hasNext())
46 {
47 bIter.next();//remove跟next是成對出現的,remove總是刪除前序
48 bIter.remove();
49 }
50 }
51 System.out.println(b);
52
53 //刪除a中所有的b中的元素
54 a.removeAll(b);
55 System.out.println(a);
56 }
57 }
最後,看到了一些關於集合架構原始碼剖析的文章,有興趣可以看一下,多瞭解內部原理也是挺好的。
各大類直接的區別可以參見:java中Map,List與Set的區別
Java集合框架原始碼剖析:HashSet 和 HashMap:
http://blog.jobbole.com/101493/
java中的HashTable,HashMap和HashSet
注意:HashMap的初始化容量為16,負載因子為0.75,然後當其中的元素個數大於16*0.75時進行擴容,每次擴容會將容量擴大兩倍。
HashSet的內部結構就是HashMap,只是每個鍵值對的值都確定為一個常量物件PRESENT(private static final Object PRESENT = new Object();)
Java集合框架原始碼剖析:LinkedHashSet 和 LinkedHashMap:
http://blog.jobbole.com/101724/
注意:LinkedHashSet 和 LinkedHashMap之間的關係和HashSet與HashMap之間的關係是一樣的。只是在其中加入了雙向連結串列的結構。插入的時候依然按照HashMap的方式插入,只是在元素之間用雙向連結串列表示前後的順序,所以查詢和插入都很快速。
Java集合框架原始碼剖析:TreeSet 和 TreeMap:
http://blog.jobbole.com/102230/
注意:TreeSet 和 TreeMap之間的關係和HashSet與HashMap之間的關係是一樣的。TreeMap實現了SortedMap介面,也就是說會按照key
的大小順序對Map中的元素進行排序,key
大小的評判可以通過其本身的自然順序(natural ordering),也可以通過構造時傳入的比較器(Comparator)。TreeMap底層通過紅黑樹(Red-Black tree)實現,也就意著containsKey()
, get()
, put()
, remove()
都有著log(n)
的時間複雜度。
紅黑樹的原理:
http://blog.jobbole.com/103045/
《Thinking In Algorithm》07.Red-Black Trees(紅黑樹)
Java集合框架原始碼剖析:ArrayList:
http://blog.jobbole.com/101796/
注意:ArrayList的初始化容量為10,然後當其中的元素個數大於16*0.75時進行擴容,每次擴容會將容量擴大原先的1.5倍,eg:第一次擴容至10+10/2=15,第二次是15+15/2 = 22,以此類推。
Java集合框架原始碼剖析:LinkedList:
http://blog.jobbole.com/101830/
注意:LinkedList中定義的雙向連結串列的Node節點如下:
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}