本文關鍵詞:
java集合框架 框架設計理念 容器 繼承層級結構 繼承圖 集合框架中的抽象類 主要的實現類 實現類特性 集合框架分類 集合框架並發包 並發實現類
什么是容器?
由一個或多個確定的元素所構成的整體叫做集合。
容器用來包裝或裝載物品的貯存器 (如箱、罐、壇)或者成形或柔軟不成形的包覆材料。
在Java中的Collection框架,有的人叫做集合有的叫做容器,不管怎么叫基本上也離不開"把元素裝起來"這個本質.
我們的世界里面豐富多彩,有各種各樣的事物,很多事物都會有他的容器
人的生活自然也離不開各種容器,喝水需要杯子,吃飯需要碗,煮湯需要鍋,這都是容器;
容器有各種各樣的形狀,也有各種各樣的特性;
比如茶杯有的有蓋子,有的沒有蓋子,水壺可以從壺嘴往外倒水等
java是面向對象的語言,萬事萬物皆是對象,縱然有着千姿百態的各種不同類型
所以想要在java對象中更加暢快的使用對象,自然也是需要容器的;
為什么要有容器?
按照容器的概念,數組也是一種容器,可以用於存放一個或者多個元素;
可是,數組只能保存同一種類型的元素,而且長度是固定的;
人們自然希望可以有一種容器能夠保存各種不同的類型的元素,並且長度是不固定的;
這也是集合框架設計的初衷;
提供一種可以保存多種類型元素,並且長度不受限制的容器,來更加方便的保存對象;
所以java中的容器也就是java世界里面承裝對象的器皿.
JAVA集合框架本質
容器根本屬性在於存/取,以及一些其他的附加的操作.
容器內部有其擺放形式:排成一行還是扔到一堆?
也有他的存取順序:先進先出還是先進后出的被壓倒最下面?
這是抽象的描述
對應到計算機科學的世界里面,那即是數據結構與算法的描述
數據結構是指相互之間存在着一種或多種關系的數據元素的集合和該集合中數據元素之間的關系組成
數據結構中有線性結構,樹形結構等,形式有隊列 棧 鍵值對 等
至此,可以這么理解編程語言中的集合框架:
集合框架目的就只是為了盛裝對象,操作對象
本質就是Java語言,針對於容器這一概念,數據結構與算法的描述實現.
更直白的說,也就只是數據結構與算法,java只是一個表現形式
比如LinkedList 他就是java語言對於雙向鏈表的一種描述,如果你懂雙向鏈表的原理,並且懂得java的語法,你也可以實現一個LinkedList
不過,選取哪些數據結構,使用哪些算法,繼承層級如何安排,這是java自己的特點;
集合框架的層級結構
當然,並不是說你用Java編寫一個雙向鏈表就是寫出來集合框架了Java是面向對象的語言,面向對象的三大基礎特征,封裝繼承多態嘛想要給一門編程語言提供一個集合框架,自然不是寫幾個算法數據結構這么簡單的事情Java中的集合框架是自頂而下設計的如同所有的對象的祖宗都是Object一樣集合框架自然也是有祖宗的,那就是Collection 這就表示集合 ,在Java中用來存儲元素的容器
不過也還有另外一派,叫做Map ,如官方文檔中描述的那樣,Map並不算是集合,只不過是一種操作數據的結構而已但是Map也提供了類似集合似的存取元素,元素操作等功能廣義上按照我們之前說的集合/容器的概念去理解的話,自然他也可以算得上是Java集合的一份子所以一般都是把Map和Collection統稱為Java的集合體系的鑒於Java語言的特性,集合體系中這些用於刻畫家族臉譜的東西,自然都是接口,除非特別指明,所提到的類型均為接口
Collection中是一組獨立的元素而Map中則是一組成對的鍵值對元素
一組獨立的元素,Collection,中又可以按照有序的列表和無序的集,這就是List 和Set當然還有Queue,隊列其實話說回來,為何Queue直接繼承自Collection?有序和無序的並集不就已經是整體了么隊列不也算是一種特殊的List么,的確隊列是一種特殊的List,而且,常用的實現類LinkedList
.....implements List<E>, Deque<E>..... 並且其中 interface Deque<E> extends Queue<E>
所以說,隊列邏輯思維意義上就是列表中比較特殊的一種,只不過他的特殊性比較多
所以在實現代碼的時候把它單獨拿出來直接繼承自Collection 作為一種大的分類
也我覺得也並沒有什么太大的違和感,個人理解,歡迎指正
現在我們已經"高屋建瓴"的把集合Collection分為了 List Set Queue這三種,再加上剛才說到的Map
- 列表(List):List集合區分元素的順序,允許包含相同的元素,訪問集合中的元素可以根據元素的索引來訪問。
- 集(Set):Set集合不區分元素的順序,不允許包含相同的元素,訪問集合中的元素只能根據元素本身來訪問(也是集合里元素不允許重復的原因)。
- 映射(Map):Map集合保存的”鍵”-“值”對,“鍵”不能重復,而且一個“鍵”只能對應一個“值”,訪問時只能根據每項元素的key來訪問其value。
- 隊列(Queue) 沒什么好理解的,就是類似隊伍的概念,詳細的以后再說
四大天王已經誕生了
Set和Map本身是無序的,在此基礎上又增加了排序的概念
所以家族里面又多出來sortedSet 和 sortedMap
既然有了排序的概念,那么在此之上繼續增加個可搜索的功能也沒什么好奇怪的
也就是
NavigableSet 和 NavigableMap
不得不說,美國人英語真好
Queue隊列中又分為:
雙端隊列Deque (double ended queue)
所以主要的接口是這些:
Collection
|---List
|---Set
|---sortedSet
|---NavigableSet
|---Queue
|---Deque
|---Map
|---sortedMap
|---NavigableMap
至此,對於java集合來說,意識形態層面的設計已經完成.
集合框架的抽象類
一人心難如萬人意,集合框架設計者也明白這個道理
自然知道提供的實現類並不能滿足所有人需求,自然有人想要自己實現,
如果從頭寫來一個自然是代價巨大,考慮到這點,集合框架提供了不少的抽象類,抽象類實現了大部分通用的方法
你想要實現,只需要繼承抽象類,並且實現必要的幾個方法即可
當然,集合的設計本身也是這個思路,一舉兩得,自己寫的這么方便的東西,沒道理不用;
抽象類大多數以Abs開頭的
AbstractCollection:
提供了Collection的主要實現
- 為了實現一個不可修改的集合,程序員只需要擴展這個類並為iterator和size 方法提供實現。(iterator方法返回的迭代器必須實現hasNext和next。)
- 為了實現一個可修改的集合,程序員必須另外重寫這個類的add方法(否則拋出一個UnsupportedOperationException),迭代器方法返回的迭代器必須另外實現它的remove方法。
Collection 下的大多數子類都繼承 AbstractCollection ,比如 List 的實現類, Set的實現類。
AbstractList
List接口的骨架實現,最大限度地減少實現由“隨機訪問”數據存儲(如數組)所支持的接口所需的工作量。
對於順序訪問數據(如鏈接列表),應該優先使用AbstractSequentialList。
AbstractSequentialList
List接口的骨架實現,以最大限度地減少實現由“順序訪問”數據存儲(如鏈接列表)支持的接口所需的工作量。
對於隨機訪問數據(如數組),應優先使用AbstractList。
AbstractSet
提供了Set接口的骨架實現,不會覆蓋AbstractCollection類中的任何實現。它只是增加了equals和hashCode的實現。
通過擴展此類來實現集合的過程與通過擴展AbstractCollection來實現集合的過程相同
不同之處在於此類的所有子類中的所有方法和構造函數都必須遵守Set接口施加的額外約束(例如,添加方法不得允許將一個對象的多個實例添加到一個集合中)。
AbstractQueue
提供了一些Queue操作的骨架實現
當基類實現不允許空元素時,此類中的實現適用。
方法add,remove和element分別基於offer,poll和peek,但是會拋出異常而不是通過false或null返回來指示失敗。
擴展此類的任何Queue實現類至少也需要定義方法Queue.offer(E),該方法不允許插入空元素
以及方法Queue.peek(),Queue.poll(),Collection.size()和Collection.iterator()。
通常,其他方法也將被覆蓋。如果這些要求不能滿足,請考慮派生AbstractCollection的子類。
AbstractMap
Map接口的骨架實現
要實現一個不可修改的映射,程序員只需要擴展這個類並為entrySet方法提供一個實現,該方法返回Map映射的set-view。
通常,返回的集合是AbstractSet的一個實現。而且一般是內部類的形式
這個集合不應該支持add或remove方法,它的迭代器不應該支持remove方法。
EnumSet
用於枚舉類型的專用Set實現
集合框架的重要實現
主要的實現類有:
Collection下面:
其中List的實現類主要是:
(1)ArrayList
List接口的可調整大小數組實現。
實現List接口中所有的可選操作,並允許任意元素,包括null。
除了實現List接口之外,該類還提供了一些方法來控制用於內部存儲列表的數組大小。(這個類大致相當於Vector,除了它是不同步的。)
此實現是不同步。
(2)LinkedList
List和Deque接口的雙端鏈表實現。實現List接口所有的可選操作,並允許任意元素(包括null)。
此實現是不同步。
(3)Vector
Vector類實現了一個可增長的對象數組。
像數組一樣,它包含可以使用整數索引訪問的組件。
不同於數組的是,Vector的大小可根據需要增大或減小,以適應在創建Vector之后添加和移除項目。
同步的
(4)Stack
Stack類表示后進先出(LIFO)對象堆棧。
它使用五個操作來擴展類Vector,這樣子可以將一個Vector視為一個堆棧。
提供了:
通常的推送和彈出操作,
以及一種方法來查看堆棧中的頂層項目,
一種方法來測試堆棧是否為空,
以及一種方法來搜索堆棧中的項目並發現它有多遠是從頂部。
當第一次創建堆棧時,它不包含任何元素。
Deque接口及其實現提供了更完整和一致的LIFO堆棧操作集,應優先使用此類。例如:
Deque<Integer> stack = new ArrayDeque<Integer>();
Set下面主要是:
(1)HashSet
這個類實現了Set接口
由一個哈希表(實際上是一個HashMap實例)支持。
它對集合的迭代次序沒有任何保證;
特別是,它不能保證順序會隨着時間的推移保持不變。這個類允許null元素。
HashSet應該是你在沒有特殊要求下的默認選擇
這個類為基本操作(添加,刪除,包含和大小)提供了恆定的時間性能,假設散列函數在桶之間正確地分散元素。
迭代此集合需要的時間與HashSet實例的大小(元素數量)加上支持HashMap實例的“容量”(桶的數量)的總和成正比。
因此,如果迭代性能很重要,不要將初始容量設置得太高(或者負載因子太低)是非常重要的。
非同步的
(2)TreeSet
基於TreeMap的NavigableSet實現。
這些元素使用它們的自然順序或者在創建集合時提供的比較器進行排序,具體取決於使用哪個構造函數。
這個實現保證:基本操作(添加,移除和包含)的時間復雜度為 log(n)非同步的
(3)LinkedHashSet
Set接口的哈希表和鏈表實現,具有可預測的迭代順序。
這個實現與HashSet的不同之處在於它保持了一個雙向鏈表,它貫穿其所有條目。
此鏈接列表定義迭代排序,即元素插入到集合中的順序(插入順序)。
請注意,如果元素重新插入到集合中,則插入順序不受影響。
(如果s.contains(e)在調用之前立即返回true,則調用s.add(e)時,將元素e重新插入到集合s中。)
非同步的
HashSet的性能總是比TreeSet好,特別是添加和查詢元素
TreeSet存在的唯一原因就是可以維持元素的排序狀態,所以,只有當需要一個排好序的Set時
才應該使用TreeSet
對於插入操作 LinkedHashSet比HashSet的代價要高
Queue的:
(1)ArrayDeque
Deque接口的可調整大小的實現。
Array deques沒有容量限制;根據使用情況動態增長.
它們不是線程安全的
在沒有外部同步的情況下,它們不支持多線程的並發訪問。
禁止使用空元素
當用作堆棧時,該類可能比Stack快,並且在用作隊列時比LinkedList快。
這個類的迭代器方法返回的迭代器是快速失敗機制的,會拋異常
ConcurrentModificationException
.
(2)PriorityQueue
基於優先級堆的無限優先級隊列
優先級隊列的元素根據其自然排序或隊列構建時提供的比較器進行排序,具體取決於使用哪個構造函數
優先級隊列不允許空元素。依賴於自然順序的優先級隊列也不允許插入非可比對象(這樣做可能導致ClassCastException)。
非同步的
優先級隊列是無界的,但具有控制用於存儲隊列中元素的數組大小的內部容量。
它總是至少與隊列大小一樣大。隨着元素被添加到優先級隊列中,其容量會自動增加。
Map下面:
(1)HashMap
基於哈希表的Map接口實現
該實現提供了所有可選的Map操作,並允許使用空值和空鍵
(HashMap類與Hashtable大致相同,只是它不同步並允許空值。)
這個類不能保證順序;而且,它不能保證順序會隨着時間的推移保持不變。
非同步的
(2)Hashtable
這個類實現了一個哈希表,它將鍵映射到值。任何非空對象都可以用作鍵或值。
要成功地從哈希表存儲和檢索對象,用作鍵的對象必須實現hashCode方法和equals方法。
一個Hashtable的實例有兩個影響其性能的參數:初始容量和負載因子
容量是哈希表中桶的數量,初始容量就是哈希表創建時的容量。
通常,默認的加載因子(.75)在時間和空間成本之間提供了一個很好的折衷。
從Java 2平台v1.2開始,該類被改型為實現Map接口,使其成為Java集合框架的成員。
與新的集合實現不同,Hashtable是同步的。
如果不需要線程安全的實現,建議使用HashMap來代替Hashtable。
如果需要線程安全的高度並行實現,則建議使用ConcurrentHashMap代替Hashtable。
(3)LinkedHashMap
Map 接口的哈希表和鏈接列表實現,具有可預知的迭代順序。
此實現與 HashMap 的不同之處在於,后者維護着一個運行於所有條目的雙重鏈接列表。
此鏈接列表定義了迭代順序,該迭代順序通常就是將鍵插入到映射中的順序(插入順序)。
注意,如果在映射中重新插入 鍵,則插入順序不受影響。
(如果在調用 m.put(k, v) 前 m.containsKey(k) 返回了 true,則調用時會將鍵 k 重新插入到映射 m 中。)
(3)TreeMap
基於紅黑樹(Red-Black tree)的 NavigableMap 實現。
該映射根據其鍵的自然順序進行排序,或者根據創建映射時提供的 Comparator 進行排序,具體取決於使用的構造方法。
此實現為 containsKey、get、put 和 remove 操作提供受保證的 log(n) 時間開銷。
這些算法是 Cormen、Leiserson 和 Rivest 的 Introduction to Algorithms 中的算法的改編。
此實現不是同步的
(4)WeakHashMap
以弱鍵 實現的基於哈希表的 Map。
在 WeakHashMap 中,當某個鍵不再正常使用時,將自動移除其條目。
更精確地說,對於一個給定的鍵,其映射的存在並不阻止垃圾回收器對該鍵的丟棄,這就使該鍵成為可終止的,被終止,然后被回收。
丟棄某個鍵時,其條目從映射中有效地移除,因此,該類的行為與其他的 Map 實現有所不同。
null 值和 null 鍵都被支持。該類具有與 HashMap 類相似的性能特征,並具有相同的效能參數初始容量 和加載因子。
像大多數 collection 類一樣,該類是不同步的。可以使用 Collections.synchronizedMap 方法來構造同步的 WeakHashMap。
(5)EnumMap
與枚舉類型鍵一起使用的專用 Map 實現。
枚舉映射中所有鍵都必須來自單個枚舉類型,該枚舉類型在創建映射時顯式或隱式地指定。
枚舉映射在內部表示為數組。此表示形式非常緊湊且高效。
(6)IdentityHashMap
此類利用哈希表實現 Map 接口,比較鍵(和值)時使用引用相等性代替對象相等性。
換句話說,在 IdentityHashMap 中,當且僅當 (k1==k2) 時,才認為兩個鍵 k1 和 k2 相等
(在正常 Map 實現(如 HashMap)中,當且僅當滿足下列條件時才認為兩個鍵 k1 和 k2 相等:(k1==null ? k2==null : e1.equals(e2)))。
此類不是 通用 Map 實現!
此類實現 Map 接口時,它有意違反 Map 的常規協定,該協定在比較對象時強制使用 equals 方法。此類設計僅用於其中需要引用相等性語義的罕見情況。
並發編程相關接口和類
基於並發編程的特性
又延伸出來下面這些接口:
java.util.concurrent包中
隊列Queue中:
阻塞隊列 BlockingQueue--生產者向隊列添加元素但隊列已滿時,生產者會被阻塞;當消費者從隊列移除元素但隊列為空時,消費者會被阻塞
雙端的阻塞隊列 BlockingDeque
阻塞隊列又細分出來的概念TransferQueue
比BlockingQueue更進一步:
生產者會一直阻塞直到所添加到隊列的元素被某一個消費者所消費(不僅僅是添加到隊列里就完事)。
新添加的transfer方法用來實現這種約束。
顧名思義,阻塞就是發生在元素從一個線程transfer到另一個線程的過程中,它有效地實現了元素在線程之間的傳遞
Map中:
ConcurrentMap 接口代表一個Map,它可以處理並發訪問。
ConcurrentMap除了繼承自java.util.Map的方法,還有一些自己的原子方法。
ConcurrentNavigableMap支持 NavigableMap 操作,且以遞歸方式支持其可導航子映射的 ConcurrentMap。
並發相關實現類 java.util.concurrent:
LinkedBlockingQueue
ArrayBlockingQueue
PriorityBlockingQueue
DelayQueue
SynchronousQueue
LinkedBlockingDeque
LinkedTransferQueue
CopyOnWriteArrayList
CopyOnWriteArraySet
ConcurrentSkipListSet
ConcurrentHashMap
ConcurrentSkipListMap
ConcurrentLinkedDeque
ConcurrentLinkedQueue
集合框架接口中使用到的一些基礎支撐類
Iterable:
實現這個接口允許對象成為 "foreach" 語句的目標。
實現了這個接口就表明已經遵從"迭代定義的規則",擁有了迭代的能力.
他是一個頂級接口:
其中:
/** * Returns an iterator over elements of type {@code T}. * * @return an Iterator. */ Iterator<T> iterator();
Iterator:
public interface Iterator<E>
想要有迭代的能力,
需要實現Iterable接口
實現接口最重要的就是提供一個 iterator() 方法,用於返回迭代器
為什么不直接實現Iterator?
首先,集合本身並不是迭代器,他只是有可以迭代的功能,所以是組合關系.
而且,如果繼承的話,那么集合框架中:
Iterator接口的核心方法next()或者hasNext()
集合對象中就會包含當前迭代位置的數據(指針)
當集合在不同方法間被傳遞時,由於當前迭代位置不可預置,那么next()方法的結果會變成不可預知
RandomAccess
標記接口
/ * * @since 1.4 */ public interface RandomAccess { }
用來表明其支持快速(通常是固定時間)隨機訪問。
主要目的是使算法能夠在隨機和順序訪問的list中表現的更加高效。
此接口的主要目的是允許一般的算法更改其行為,從而在將其應用到隨機或連續訪問列表時能提供良好的性能。
比如:
Collections下的binarySearch方法的源碼:
public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c) { if (c==null) return binarySearch((List<? extends Comparable<? super T>>) list, key); if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD) return Collections.indexedBinarySearch(list, key, c); else return Collections.iteratorBinarySearch(list, key, c); }
從中可以清晰的看到,這個RandomAccess標記的作用
java集合框架中的所有具體類中都實現了Cloneable和Serializable接口
因此它們的實例都是可復制且可序列化的。
Serializable
public interface Serializable { }