032.[轉] Java集合框架

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中。

參考資料

相關文章
相關標籤/搜索