Java語言集合框架提供一系列集合接口類 (collection interface)和實現類,知足對集合中元素對象的各類集合抽象操做。java
1. 集合接口類Collection/List/Set/Map
下圖顯示了在包java.util.*中主要的核心集合接口類,git
上圖中的Collection/List/Set/Map等都是泛型接口,各自的定義和做用以下:編程
- Collection是集合的頂層接口定義,其它的集合類都繼承於Collection(除Map),這個接口定義了對集合元素的增刪改查,及其提供interator用於循環整個集合中的元素列表等等。
- Set是一個不能有重複元素的集合。
- List是一個有序元素集合,有序是指按照加入/插入數組位置的順序,集合中能夠有重複的元素,能夠認爲List就是一個數組,訪問時能夠經過位置index直接訪問元素。
- Map是對鍵值對(key/value)元素的集合,集合中不能有重複的key。
- Queue/Deque是一個提供可以進行隊列操做的集合,好比FIFO(先進先出)/ LIFO(後進先出)。
- SortedSet/SortedMap是一個按照元素進行升序排列的集合,集合中的元素排序取決於Comparator的實現。
2. 集合實現類和工具類
集合的實現類不少,JDK中提供的實現類都在java.util.*包中,其中List/Set/Map有以下幾個實現類,後端
- List
- ArrayList/LinkedList/Vector
- Map
- HashMap/LinkedHashMap/TreeMap/WeakHashMap
- Hashtable/ConcurrentHashMap
- Set
- HashSet/LinkedHashSet/TreeSet
集合的工具類Collections/Arrays提供一些集合的複製、比較、排序和轉換操做,數組
- Utilities
- Collections/Arrays
上述各個實現類和接口類的關係見下圖,安全
3. 集合實現類的特色和數據結構
下面以表格形式列出各個實現類的特色和區別,方便進行對比,其中不一樣的數據結構決定了各個集合實現類的不一樣性能特色,詳細的數據結構描述見後面註解。數據結構
集合接口 | 集合實現類 | 是否按插入順序存放 | 是否按有序存放(注1) | 是否能夠存放重複元素 | 是否線程安全 | 數據結構特性描述 |
List | ArrayList | Yes | No | Yes | No | 基於動態數組的數據結構,注2 |
LinkedList | Yes | No | Yes | No | 基於雙向鏈表的數據結構,查詢慢,插入快,注3。 | |
Vector | Yes | No | Yes | Yes* | Deprecated,注4。 | |
Map | HashMap | No | No | Yes | No | 基於哈希表的元素數據離散分佈,注5。 |
LinkedHashMap | No | No | Yes | No | 基於哈希表的元素數據離散分佈,除此以外集合元素數據之間有雙向鏈表指針,注6。 | |
TreeMap | No | Yes | Yes | No | 基於紅黑樹的數據結構,元素須要提供Comparator實現,用於有序存放,注7。 | |
WeakHashMap | No | No | Yes | No | ||
Hashtable | No | No | Yes | Yes | 基於哈希表的元素數據分散分佈,經過對象鎖實現線程安全 | |
ConcurrentHashMap | No | No | Yes | Yes | 經過lock實現線程安全,在多線程環境中比Hashtable有更好的併發性能 | |
Set | HashSet | No | No | No | No | 底層使用HashMap實現 |
LinkedHashSet | Yes | No | No | No | 底層使用LinkedHashMap實現 | |
TreeSet | No | Yes | No | No | 底層使用TreeMap實現,元素須要提供Comparator實現,用於有序存放 |
注1:元素是否按有序存放,是指集合中元素根據Comparator進行比較後升序排列。多線程
注2:ArrayList是基於動態數組的數據結構,數據插入時,會致使整個後端數據的日後移動一位,因此插入速度慢,可是根據位置索引能夠直接訪問元素數據,因此經過位置索引查詢數據速度會很快。併發
注3:LinkedList是基於雙向鏈表的數據結構,插入快,可是查詢會比較慢。另外LinkedList支持getFirst/getLast/removeFirst/removeLast/addFirst/addLast操做,能夠方便實現FIFO/LIFO隊列操做。雙向鏈表的數據結構以下圖所示,
注4:Vector因爲其在全部的get/set上進行了synchronize,致使難於在併發編程發揮做用,在不少時候可使用List list = Collections.synchronizedList(new ArrayList())方法取代,目前不建議使用Vector來用於線程安全的編程。
注5:HashMap基於哈希表的元素數據離散分佈,是指數據按照必定規則進行離散,好比數值按16取模,各自落入不一樣的子集合,所以數據元素排列插入後無序,見下圖所示,
注6:LinkedHashMap在集合元素數據之間有雙向鏈表指針,數據的刪除和插入快,數據元素排列後無序,見下圖所示,
注7:TreeMap基於紅黑樹(近平衡二叉樹)的數據結構,這個數據結構最大的特色是各個元素數據達到平衡分佈,最遠和最近葉子節點離根節點距離相差不超1,使得元素的查找和插入速度達到O(log N)級別。
4. 集合實現類的插入操做
咱們能夠嘗試爲各個集合實現類進行插入數據操做,而後查看數據元素在集合中的數據排列,下面主要觀察數據排列是否有序,是否按照插入順序排列。經過觀察,能夠更加深刻地瞭解各個實現類的數據結構和特性。
List的數據插入
下面的代碼新建了三個List實現類實例,而後依次插入10個隨機數,最後打印出列表數據。
List<Integer> list1 = new ArrayList<>(); List<Integer> list2 = new LinkedList<>(); List<Integer> list3 = new Vector<>(); for (int i = 0; i < 10; i++) { //產生一個隨機數,並將其放入List中 int value = (int) (Math.random() * 100); App.logMessage("第 " + i + " 次產生的隨機數爲:" + value); list1.add(value); list2.add(value); list3.add(value); } App.logMessage("ArrayList:" + list1); App.logMessage("LinkedList:" + list2); App.logMessage("Vector:" + list3);
結果以下,請觀察元素插入和排列順序,
第 0 次產生的隨機數爲:41
第 1 次產生的隨機數爲:68
第 2 次產生的隨機數爲:62
第 3 次產生的隨機數爲:4
第 4 次產生的隨機數爲:18
第 5 次產生的隨機數爲:38
第 6 次產生的隨機數爲:97
第 7 次產生的隨機數爲:9
第 8 次產生的隨機數爲:19
第 9 次產生的隨機數爲:1
ArrayList:[41, 68, 62, 4, 18, 38, 97, 9, 19, 1]
LinkedList:[41, 68, 62, 4, 18, 38, 97, 9, 19, 1]
Vector:[41, 68, 62, 4, 18, 38, 97, 9, 19, 1]
能夠看到,各個List的數據元素排列和插入順序一致,這也是因爲動態數組的數據結構帶來的特性。
Set的數據插入
下面的代碼新建了三個Set實現類實例,而後依次插入10個隨機數,最後打印出列表數據。
Set<Integer> set1 = new HashSet<>(); Set<Integer> set2 = new LinkedHashSet<>(); Set<Integer> set3 = new TreeSet<>(); for (int i = 0; i < 10; i++) { //產生一個隨機數,並將其放入Set中 int value = (int) (Math.random() * 100); App.logMessage("第 " + i + " 次產生的隨機數爲:" + value); set1.add(value); set2.add(value); set3.add(value); } App.logMessage("HashSet:" + set1); App.logMessage("LinkedHashSet:" + set2); App.logMessage("TreeSet :" + set3);
結果以下,請觀察元素插入和排列順序,
第 0 次產生的隨機數爲:51
第 1 次產生的隨機數爲:86
第 2 次產生的隨機數爲:24
第 3 次產生的隨機數爲:66
第 4 次產生的隨機數爲:76
第 5 次產生的隨機數爲:59
第 6 次產生的隨機數爲:13
第 7 次產生的隨機數爲:34
第 8 次產生的隨機數爲:89
第 9 次產生的隨機數爲:21
HashSet:[66, 34, 51, 21, 86, 24, 89, 59, 76, 13]
LinkedHashSet:[51, 86, 24, 66, 76, 59, 13, 34, 89, 21]
TreeSet :[13, 21, 24, 34, 51, 59, 66, 76, 86, 89]
能夠看到,HashSet/LinkedHashSet無序,TreeSet按大小依次排列。這是因爲HashSet/LinkedHashSet/TreeSet底層實現用的是HashMap/LinkedHashMap/TreeMap,所以繼承了各自的特色。
Map的數據插入
下面的代碼新建了五個Map實現類實例,而後依次插入10個隨機數(隨機數爲key),最後打印出列表數據。
Map map1 = new HashMap(); Map map2 = new TreeMap(); Map map3 = new LinkedHashMap(); Map map4 = new WeakHashMap(); Map map5 = new Hashtable(); Map map6 = new ConcurrentHashMap(); for (int i = 0; i < 10; i++) { //產生一個隨機數,並將其放入map中 int value = (int) (Math.random() * 100); App.logMessage("第 " + i + " 次產生的隨機數爲:" + value); if (!map1.containsKey(value)){ map1.put(value, i); map2.put(value, i); map3.put(value, i); map4.put(value, i); map5.put(value, i); map6.put(value, i); } } App.logMessage("產生的隨機數爲key,index爲value"); App.logMessage("HashMap:" + map1); App.logMessage("TreeMap:" + map2); App.logMessage("LinkedHashMap:" + map3); App.logMessage("WeakHashMap:" + map4); App.logMessage("Hashtable:" + map5); App.logMessage("ConcurrentHashMap:" + map5);
結果以下,請觀察元素插入和排列順序,
第 0 次產生的隨機數爲:48
第 1 次產生的隨機數爲:86
第 2 次產生的隨機數爲:81
第 3 次產生的隨機數爲:19
第 4 次產生的隨機數爲:90
第 5 次產生的隨機數爲:74
第 6 次產生的隨機數爲:55
第 7 次產生的隨機數爲:29
第 8 次產生的隨機數爲:89
第 9 次產生的隨機數爲:65
產生的隨機數爲key,index爲value
HashMap:{48=0, 81=2, 65=9, 19=3, 86=1, 55=6, 89=8, 90=4, 74=5, 29=7}
TreeMap:{19=3, 29=7, 48=0, 55=6, 65=9, 74=5, 81=2, 86=1, 89=8, 90=4}
LinkedHashMap:{48=0, 86=1, 81=2, 19=3, 90=4, 74=5, 55=6, 29=7, 89=8, 65=9}
WeakHashMap:{90=4, 74=5, 89=8, 29=7, 65=9, 55=6, 81=2, 86=1, 48=0, 19=3}
Hashtable:{90=4, 89=8, 65=9, 19=3, 86=1, 81=2, 55=6, 29=7, 74=5, 48=0}
ConcurrentHashMap:{90=4, 89=8, 65=9, 19=3, 86=1, 81=2, 55=6, 29=7, 74=5, 48=0}
能夠看到,TreeMap按key大小升序排列,LinkedHashMap按value大小升序排列,其它無序。
集合實現類的性能比較
爲了比較各個集合實現類的性能,對各個集合實現類進行以下三個操做,
- 插入:在集合中插入0-N個數據元素,N爲指定性能測試要達到的數組大小
- 查詢:在集合中一一查詢0-N個數據元素,N爲指定性能測試的數組大小,換句話說輪詢集合中全部元素
- 刪除:在集合中一一刪除0-N個數據元素,N爲指定性能測試的數組大小,換句話說刪除集合中全部元素
測試數組大小分別爲1萬、5萬、10萬、15萬,線性倍增,而後觀察各個集合實現類在不一樣數組大小下的性能表現。
注:在Map中查詢分別添加了經過key/value查詢的測試。
測試環境:Java版本爲1.8.0_111,主機環境:Win7 SP1 x64, intel i5-6600K 4 cores 3.5GHz CPU, 16G memory。
能夠看到,
- List集合實現類在插入、查詢、刪除操做上,隨着數組變大有明顯的性能開銷增長,性能開銷的增長倍數超過數組大小的增長倍數。
- Map集合實現類在經過value查詢性能開銷很大,在實際編程中,儘可能避免此類操做
- 表中有些操做時間開銷都在10毫秒內,隨着數組倍增,性能表現不錯,這些操做能夠在編程中多加利用。
測試程序代碼
演示代碼倉庫地址:https://gitee.com/pphh/blog,能夠經過以下git clone命令獲取倉庫代碼,
git clone git@gitee.com:pphh/blog.git
上述測試程序代碼樣例在文件路徑171117_java_collection\demo中。
參考資料
- Java Tutorials - Collection介紹
- Java Tutorials - Collection Interfaces介紹
- wiki - 紅黑樹