java集合框架的实现
集合框架的具體實現
集合框架的實現有如下幾類:
- 通用實現
- 特殊實現
- 并發實現
- 包裝實現
- 簡便實現
- 抽象實現
Set實現
Set實現分為通用和特殊實現
通用實現
通用實現有三種:
- HashSet
- TreeSet
- LinkedHashSet
HashSet比TreeSet快的多,它們的時間復雜度大約是O(1) vs O(logn),但是前者是無序的。如果你需要SortedSet接口中的操作,或者要求元素有序的迭代,使用TreeSet。LinkedHashSet介于HashSet和TreeSet之間,它是鏈表哈希表實現,提供按插入順序迭代,并且運行幾乎和HasSet一樣快。
對于HashSet,需要記住的是,它的迭代時間和條目數及容量有關,是線性的。因此,選擇太大的初始容量既浪費空間,也浪費時間。但選擇太小的初始容量,也會導致每次擴容時浪費時間在拷貝數據結構上。如果不分配初始容量,默認值是16。手動指定時,建議選擇2的冪次,大小則是預期容量增長的2倍,比如:
Set<String> s = new HashSet<>(64);而LinkedHashSet的迭代時間不受容量的影響。
特殊實現
特殊實現有兩種:
- EnumSet 枚舉集合
- CopyOnWriteArraySet 寫時復制數組集合
EnumSET對針對枚舉類型的高性能集合實現。它提供了一個靜態工程方法range支持按范圍迭代:
for (Day d : EnumSet.range(Day.MONDAY, Day.FRIDAY))System.out.println(d);EnumSet對傳統的位標志也提供了一種豐富的,類型安全的替代:
EnumSet.of(Style.BOLD, Style.ITALIC)CopyOnWriteArraySet是寫時復制數組(copy-on-write)備份的集合實現。所有的可變操作,比如add,set,remove,都是通過創建一個新的數組拷貝實現的,不需要用鎖。即使是迭代,也可以安全的與元素的插入刪除同時進行。與大部分集合實現不同的是,它的add,remove,contains方法的用時和集合的大小有關。它僅適用于需要頻繁迭代但極少修改的集合,比如維護事件處理列表。
List實現
List實現分為通用和特殊實現
通用實現
有兩種:
- ArrayList
- LinkedList
ArrayList使用的最多,它提供時間復雜度為o(1)的位置訪問。它不用為列表中的每個元素分配節點對象,并且在同時移動多個元素時,可以利用System.arraycopy優勢。可以將ArrayList當作沒有同步的Vector。
如果你經常在列表的頭部添加元素,或者在迭代時刪除元素,可以考慮使用LinkedList,這些操作在LinkedList的時間復雜度是o(1),但是在ArrayList是線性的。但如果是位置訪問,則恰好相反。
ArrayList有一個可選的參數,初始容量,LinkedList沒有初始容量參數,但是實現了隊列接口,支持如下7種可選方法:clone, addFirst, getFirst, removeFirst, addLast, getLast, removeLast。
特殊實現
- CopyOnWriteArrayList
CopyOnWriteArrayList是寫時復制數組(copy-on-write)備份的列表實現,和CopyOnWriteArraySet相似。即使在迭代時,也不需要同步,并且迭代器從來不會拋出ConcurrentModificationException異常。這個實現適合維護事件處理列表,這種很少修改,但頻繁遍歷的情況。
如果你需要同步,那Vector比ArrayList的同步版Collections.synchronizedList還要快一點,但是它會載入很多過時的操作,因此要小心使用。
如果列表大小固定,也不需要刪除、增加或其他批量操作,那么簡化實現的Arrays.asList值得考慮。·
###Map實現
分為通用實現,特殊實現,以及并發實現
通用實現
有三種:
- HashMap
- TreeMap
- LinkedHashMap
如果你需要SortedMap接口中的操作,或者按照鍵有序對集合試圖進行迭代,請使用TreeMap;如果你在意速度,并不關心迭代的順序,請使用HashMap;如果你需要接近HasMap的性能,并且按照插入順序迭代,請使用LinkedHashMap。從這方面來說,LinkedHashMap和LinkedHashSet很像。
LinkedHashMap有兩項能力LinkedHashSet不具備。你可以基于key的訪問而不是插入來排序,換句話說,只是查找某個key的值,就會導致這個key被重新排序(原文是帶到map的末尾)。另外LinkedHashMap提供了removeEldestEntry方法,可以重寫該方法來實現插入新的映射時自動移除最舊的映射的策略。利用這點可以很容易實現自定義的緩存。
比如,下面的示例維持map中最多100條映射:
private static final int MAX_ENTRIES = 100;protected boolean removeEldestEntry(Map.Entry eldest) {return size() > MAX_ENTRIES; }特殊實現
有三種:
- EnumMap
- WeakHashMap
- IdentityHashMap
EnumMap內部是作為數組實現的,它為枚舉鍵的使用提供了高性能的Map實現。它將Map接口的豐富性、安全性與數組的訪問速度結合起來。因此,如果你想將枚舉映射到值,請優先考慮使用EnumMap。
WeakHashMap只存儲鍵的弱引用(不增加引用計數,類似python中弱引用),這樣當某個鍵在WeakHashMap外部不再被引用時,其鍵值對就會被垃圾回收。WeakHashMap是利用弱引用功能的最簡單方式,可用于實現類似注冊表這種數據結構,當任何線程都無法訪問其鍵時,條目的實用性就會消失。
IdentityHashMap基于身份的Map實現,比較抽象,暫不討論。
并發實現
一種:
- ConcurrentHashMap
java.util.concurrent包中的ConcurrentMap接口,有原子性的putIfAbsent, remove, replace方法,ConcurrentHashMap實現了這個接口。
ConcurrentHashMap是由哈希表支持的高并發,高性能實現。執行檢索時它不會阻塞,并且允許客戶端選擇更新的并發級別。它用于替代Hashtable,除了實現了ConcurrentMap接口,它支持Hashtable中所有的舊方法。但是不建議繼續調用這些過時的方法。
Queue實現
分為通用實現和并發實現
通用實現
兩種:
- LinkedList
- PriorityQueue
LinkedList實現了Queue接口,提供先入先出的隊列操作。
PriorityQueue是基于堆這種數據結構的優先級隊列。它根據構造時指定的順序(可以是自然排序,或Comparator對象),對元素進行排序。
隊列的檢索操作:poll, remove, peek, 和element,訪問隊列頭部的元素,也就是排序的最小值。如果多個元素都是最小值,那么頭部是其中某個元素。
PriorityQueue及它的迭代器實現了Collection及Iterator接口中所有可選可選方法。但是迭代器不保證以特定的順序遍歷PriorityQueue中的元素。如果需要有序迭代,可以考慮使用Arrays.sort(pq.toArray())。
并發實現
java.util.concurrent包含許多同步的(線程安全的)隊列接口和類。BlockingQueue接口實現了檢索元素時,等待隊列非空,存儲元素時,等待空間可用,下面的這些類實現了該接口:
- LinkedBlockingQueue 鏈表實現的FIFO隊列
- ArrayBlockingQueue 數組實現的FIFO隊列
- PriorityBlockingQueue 堆實現的優先級隊列
- DelayQueue 堆實現的基于時間的調度隊列
- SynchronousQueue 使用BlockingQueue接口的簡單機制(不明所以)
Deque實現
分為通用實現和并發實現
通用實現
兩種:
- LinkedList
- ArrayDeque
Deque接口支持從兩端插入、移除及檢索元素,它的基本方法是:addFirst, addLast, removeFirst, removeLast, getFirst, getLast。
LinkedList更靈活,實現了列表的所有可選操作,并且允許null元素。LinkedList最適合的操作是迭代時移除元素。但它消耗更多內存。
ArrayDeque是可調整大小的數組實現,如果考慮效率的話,ArrayDeque的插入和移除操作效率更高。
可以通過兩種方式便利ArrayDeque:
foreach
ArrayDeque<String> aDeque = new ArrayDeque<String>();. . . for (String str : aDeque) {System.out.println(str); }Iterator
ArrayDeque<String> aDeque = new ArrayDeque<String>(); . . . for (Iterator<String> iter = aDeque.iterator(); iter.hasNext(); ) {System.out.println(iter.next()); }并發實現
一種:
- LinkedBlockingDeque
如果雙向隊列為空,方法takeFirst, takeLast會等待,直到元素可用。
包裝實現
包裝實現將任務委派給具體的集合,但是會添加一些額外的功能。按設計模式來說,它算是裝飾器模式。包裝實現是匿名的,它們提供靜態工廠方法,而不是公眾類。你可以在Collections類中找到這些實現。
同步包裝
六個核心集合接口:Collection, Set, List, Map, SortedSet, SortedMap,都有靜態工廠方法:
public static <T> Collection<T> synchronizedCollection(Collection<T> c); public static <T> Set<T> synchronizedSet(Set<T> s); public static <T> List<T> synchronizedList(List<T> list); public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m); public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s); public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m);每個靜態工廠方法都返回一個同步的(線程安全的)集合。為了確保順序訪問,對原集合的所有訪問都必須通過這個返回的集合進行。確保這一點的最容易的方式就是,不要保留原來的集合引用,比如,我們可以用下面這個方式創建一個同步集合:
List<Type> list = Collections.synchronizedList(new ArrayList<Type>());在面對并發訪問時, 用戶必須手動同步正在迭代的同步集合。因為迭代通過多次調用集合完成,這必須被整合為一個原子性操作。下面是迭代一個同步的包裝集合的慣用方式:
Collection<Type> c = Collections.synchronizedCollection(myCollection); synchronized(c) {for (Type e : c)foo(e); }注意,這里使用了synchronized關鍵字來手動同步,迭代必須在synchronized代碼塊內進行,否則會導致不可預料的行為。迭代集合的視圖與此類似,也需要手動同步,比如:
Map<KeyType, ValType> m = Collections.synchronizedMap(new HashMap<KeyType, ValType>());... Set<KeyType> s = m.keySet();... // Synchronizing on m, not s! synchronized(m) {while (KeyType k : s) // 使用while進行迭代foo(k); }注意,手動同步是在同步的Map上進行,而不是它的視圖。
不可修改包裝
不可修改包裝會攔截對集合的修改操作,并拋出UnsupportedOperationException異常。它們的兩種主要作用是:
- 使集合一旦創建便不可修改
- 允許特定的客戶端以只讀模式訪問你的數據結構。你仍然可以保持對原集合的引用,但是暴露包裝的引用。通過這種方式,客戶端只能看不能改,而你擁有完全的訪問權限。
和同步包裝類似,六個核心的集合接口都有靜態工廠方法:
public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c); public static <T> Set<T> unmodifiableSet(Set<? extends T> s); public static <T> List<T> unmodifiableList(List<? extends T> list); public static <K,V> Map<K, V> unmodifiableMap(Map<? extends K, ? extends V> m); public static <T> SortedSet<T> unmodifiableSortedSet(SortedSet<? extends T> s); public static <K,V> SortedMap<K, V> unmodifiableSortedMap(SortedMap<K, ? extends V> m);接口檢查包裝
Collections.checked接口包裝用于泛型集合。它們返回指定集合的動態類型安全的視圖。如果客戶端嘗試添加錯誤類型的元素,視圖會拋出ClassCastException異常。java的泛型機制提供了編譯時的類型檢查,但是這一機制還是可能會被打破。而動態類型安全視圖可以徹底干掉這種可能。
簡便實現
如果你不需要通用實現的全部能力,那么簡便實現更方便,更高效。
數組的列表視圖
Arrays.asList 方法返回其數組參數的列表視圖。對列表的修改會寫回數組,反之也一樣。列表的大小固定,等于數組的長度。對列表調用add和remove方法,會拋出UnsupportedOperationException異常。
列表視圖是基于數組的接口與基于集合的接口之間的橋梁。它允許你傳一個數組給一個期望集合的方法。另外,它的還有一個用處,如果你需要一個定長的列表,那么它比起通用實現更高效。下面是它的慣用法:
List<String> list = Arrays.asList(new String[size]);不可變重復元素列表
偶爾你需要一個不可變的列表,它包含同樣的元素的多次重復。Collections.nCopies方法就返回這樣一個列表。它有兩種用途,一是用來初始化一個剛創建的列表。比如,你想要一個包含一百個1元素的列表,你可以這樣做:
List<Integer> list = new ArrayList<>(Collections.nCopies(100, 1));第二種用法是擴充一個列表。假如你想將上面的列表再添加100個2進去,你可以這樣做:
list.addAll(Collections.nCopies(100, 2));不可變單元素集合
有時候你可能需要一個不可變的Set,且它只包含單個指定的元素。Collections.singleton方法就返回這樣一個Set。它的一個用法是從集合中移除指定元素的所有出現,比如還是上面的列表,將剛添加進去的2全部移除
list.removeAll(Collections.singleton(2));另一個慣用法是從一個Map中移除某些value值一樣的所有元素。比如你有個Map,它記錄每個人與其職業的對應關系,現在你想移除所有的律師,你可以這樣做:
job.values().removeAll(Collections.singleton(LAWYER));還有一個用法是給接收集合的方法提供單個值。
空Set, List, Map常量
emptySet, emptyList, emptyMap方法返回對應類型的空集合。
總結
以上是生活随笔為你收集整理的java集合框架的实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 提升销售人员的信息处理能力
- 下一篇: hadoop--HDFS_机架感知与网络