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