Java集合工具包位于Java.util包下,包含了很多常用的數據結構,如數組、鏈表、棧、隊列、集合、哈希表等。學習Java集合框架下大致可以分為如下五個部分:List列表、Set集合、Map映射、迭代器(Iterator、Enumeration)、工具類(Arrays、Collections)。
Java集合類的整體框架如下:
Java集合工具包位于Java.util包下,包含了很多常用的數據結構,如數組、鏈表、棧、隊列、集合、哈希表等。學習Java集合框架下大致可以分為如下五個部分:List列表、Set集合、Map映射、迭代器(Iterator、Enumeration)、工具類(Arrays、Collections)。
Collection是List、Set等集合高度抽象出來的接口,它包含了這些集合的基本操作,它主要又分為兩大部分:List和Set。
List接口通常表示一個列表(數組、隊列、鏈表、棧等),其中的元素可以重復,常用實現類為ArrayList和LinkedList,另外還有不常用的Vector。另外,LinkedList還是實現了Queue接口,因此也可以作為隊列使用。
Set接口通常表示一個集合,其中的元素不允許重復(通過hashcode和equals函數保證),常用實現類有HashSet和TreeSet,HashSet是通過Map中的HashMap實現的,而TreeSet是通過Map中的TreeMap實現的。另外,TreeSet還實現了SortedSet接口,因此是有序的集合(集合中的元素要實現Comparable接口,并覆寫Compartor函數才行)。
我們看到,抽象類AbstractCollection、AbstractList和AbstractSet分別實現了Collection、List和Set接口,這就是在Java集合框架中用的很多的適配器設計模式,用這些抽象類去實現接口,在抽象類中實現接口中的若干或全部方法,這樣下面的一些類只需直接繼承該抽象類,并實現自己需要的方法即可,而不用實現接口中的全部抽象方法。
Map是一個映射接口,其中的每個元素都是一個key-value鍵值對,同樣抽象類AbstractMap通過適配器模式實現了Map接口中的大部分函數,TreeMap、HashMap、WeakHashMap等實現類都通過繼承AbstractMap來實現,另外,不常用的HashTable直接實現了Map接口,它和Vector都是JDK1.0就引入的集合類。
Iterator是遍歷集合的迭代器(不能遍歷Map,只用來遍歷Collection),Collection的實現類都實現了iterator()函數,它返回一個Iterator對象,用來遍歷集合,ListIterator則專門用來遍歷List。而Enumeration則是JDK1.0時引入的,作用與Iterator相同,但它的功能比Iterator要少,它只能再Hashtable、Vector和Stack中使用。
Arrays和Collections是用來操作數組、集合的兩個工具類,例如在ArrayList和Vector中大量調用了Arrays.Copyof()方法,而Collections中有很多靜態方法可以返回各集合類的synchronized版本,即線程安全的版本,當然了,如果要用線程安全的結合類,首選Concurrent并發包下的對應的集合類。
轉載出處:http://blog.csdn.net/ns_code/article/details/35564663
Collection
|-----List 有序(存儲順序和取出順序一致),可重復
|----ArrayList ,線程不安全,底層使用數組實現,查詢快,增刪慢。
效率高。每次容量不足時,自增長度的一半,如下源碼可知
int newCapacity = oldCapacity + (oldCapacity >> 1);
|----LinkedList , 線程不安全,底層使用鏈表實現,查詢慢,增刪快。效率高。
|----Vector , 線程安全,底層使用數組實現,查詢快,增刪慢。效率低。
每次容量不足時,默認自增長度的一倍(如果不指定增量的話),如下源碼可知
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);
|-----Set 元素唯一,一個不包含重復元素的 collection。
更確切地講,set 不包含滿足 e1.equals(e2) 的元素對 e1 和 e2,并且最多包含一個 null 元素。
|--HashSet 底層是由HashMap實現的,通過對象的hashCode方法與equals方法來保證插入元素的唯一性,無序(存儲順序和取出順序不一致)。
|--LinkedHashSet 底層數據結構由哈希表和鏈表組成。哈希表保證元素的唯一性,鏈表保證元素有序。(存儲和取出是一致)
|--TreeSet 基于 TreeMap 的 NavigableSet 實現。
使用元素的自然順序對元素進行排序,或者根據創建 set 時提供的 Comparator 進行排序,具體取決于使用的構造方法。元素唯一。
HashMap 和 Hastable 的區別
HashMap幾乎可以等價于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受為null的鍵值(key)和值(value),而Hashtable則不行)。
HashMap是非synchronized,而Hashtable是synchronized,這意味著Hashtable是線程安全的,多個線程可以共享一個Hashtable;而如果沒有正確的同步的話,多個線程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的擴展性更好。
另一個區別是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以當有其它線程改變了HashMap的結構(增加或者移除元素),將會拋出ConcurrentModificationException,但迭代器本身的remove()方法移除元素則不會拋出ConcurrentModificationException異常。但這并不是一個一定發生的行為,要看JVM。這條同樣也是Enumeration和Iterator的區別。
由于Hashtable是線程安全的也是synchronized,所以在單線程環境下它比HashMap要慢。如果你不需要同步,只需要單一線程,那么使用HashMap性能要好過Hashtable。
HashMap不能保證隨著時間的推移Map中的元素次序是不變的。
HashMap采用Entry數組來存儲key-value對,每一個鍵值對組成了一個Entry實體,Entry類實際上是一個單向的鏈表結構,它具有Next指針,可以連接下一個Entry實體,依次來解決Hash沖突的問題,因為HashMap是按照Key的hash值來計算Entry在HashMap中存儲的位置的,如果hash值相同,而key內容不相等,那么就用鏈表來解決這種hash沖突。
ConcurrentHashMap與HashMap相比,有以下不同點
ConcurrentHashMap線程安全,而HashMap非線程安全
HashMap允許Key和Value為null,而ConcurrentHashMap不允許
HashMap不允許通過Iterator遍歷的同時通過HashMap修改,而ConcurrentHashMap允許該行為,并且該更新對后續的遍歷可見
Java 8基于CAS的ConcurrentHashMap
注:本章的代碼均基于JDK 1.8.0_111
數據結構
Java 7為實現并行訪問,引入了Segment這一結構,實現了分段鎖,理論上最大并發度與Segment個數相等。Java 8為進一步提高并發性,摒棄了分段鎖的方案,而是直接使用一個大的數組。同時為了提高哈希碰撞下的尋址性能,Java 8在鏈表長度超過一定閾值(8)時將鏈表(尋址時間復雜度為O(N))轉換為紅黑樹(尋址時間復雜度為O(long(N)))。
尋址方式
Java 8的ConcurrentHashMap同樣是通過Key的哈希值與數組長度取模確定該Key在數組中的索引。同樣為了避免不太好的Key的hashCode設計,它通過如下方法計算得到Key的最終哈希值。不同的是,Java 8的ConcurrentHashMap作者認為引入紅黑樹后,即使哈希沖突比較嚴重,尋址效率也足夠高,所以作者并未在哈希值的計算上做過多設計,只是將Key的hashCode值與其高16位作異或并保證最高位為0(從而保證最終結果為正整數)
CopyOnWriteArrayList如何做到線程安全的
CopyOnWriteArrayList使用了一種叫寫時復制的方法,當有新元素添加到CopyOnWriteArrayList時,先從原有的數組中拷貝一份出來,然后在新的數組做寫操作,寫完之后,再將原來的數組引用指向到新數組。
當有新元素加入的時候,如下圖,創建新數組,并往新數組中加入一個新元素,這個時候,array這個引用仍然是指向原數組的。