Java集合型別詳解
這篇文章總結了所有的Java集合(Collection)。主要介紹各個集合的特性和用途,以及在不同的集合型別之間轉換的方式。
Arrays
Array是Java特有的陣列。在你知道所要處理資料元素個數的情況下非常好用。java.util.Arrays
包含了許多處理資料的實用方法:
- Arrays.asList:可以從
Array
轉換成List
。可以作為其他集合型別構造器的引數。 - Arrays.binarySearch:在一個已排序的或者其中一段中快速查詢。
- Arrays.copyOf:如果你想擴大陣列容量又不想改變它的內容的時候可以使用這個方法。
- Arrays.copyOfRange:可以複製整個陣列或其中的一部分。
- Arrays.deepEquals
、
Arrays.deepHashCode:Arrays.equals/hashCode
的高階版本,支援子陣列的操作。 - Arrays.equals:如果你想要比較兩個陣列是否相等,應該呼叫這個方法而不是陣列物件中的
equals
方法(陣列物件中沒有重寫equals()
方法,所以這個方法之比較引用而不比較內容)。這個方法集合了Java 5的自動裝箱和無參變數的特性,來實現將一個變數快速地傳給equals()
方法——所以這個方法在比較了物件的型別之後是直接傳值進去比較的。 - Arrays.fill:用一個給定的值填充整個陣列或其中的一部分。
- Arrays.hashCode:用來根據陣列的內容計算其雜湊值(陣列物件的
hashCode()
不可用)。這個方法集合了Java 5的自動裝箱和無參變數的特性,來實現將一個變數快速地傳給Arrays.hashcode
方法——只是傳值進去,不是物件。 - Arrays.sort:對整個陣列或者陣列的一部分進行排序。也可以使用此方法用給定的比較器對物件陣列進行排序。
- Arrays.toString:列印陣列的內容。
如果想要複製整個陣列或其中一部分到另一個陣列,可以呼叫 System.arraycopy
方法。此方法從源陣列中指定的位置複製指定個數的元素到目標陣列裡。這無疑是一個簡便的方法。(有時候用 ByteBuffer bulk複製會更快。可以參考這篇文章).
最後,所有的集合都可以用T[] Collection.toArray( T[] a )
這個方法複製到陣列中。通常會用這樣的方式呼叫:
return coll.toArray( new T[ coll.size() ] );
這個方法會分配足夠大的陣列來儲存所有的集合,這樣 toArray
在返回值時就不必再分配空間了。
單執行緒集合
這一部分介紹的是不支援多執行緒的集合。這些集合都在java.util
包裡。其中一些在Java 1.o的時候就有了(現在已經棄用),其中大多數在Java 1.4中重新發布。列舉集合在Java 1.5中重新發布,並且從這個版本之後所有的集合都支援泛型。PriorityQueue
也在Java 1.5中加入。非執行緒安全的集合架構的最後一個版本是ArrayDeque
,也在Java 1.6中重新發布了。
List
- ArrayList:最有用的
List
集合實現。由一個整形數字或陣列儲存了集合的大小(陣列中第一個沒有使用的元素)。像所有的List
集合一樣,ArrayList
可以在必要的時候擴充套件它的大小。ArrayList訪問元素的時間開銷固定。在尾部新增元素成本低(為常數複雜度),而在頭部新增元素成本很高(線性複雜度)。這是由ArrayList
的實現原理——所有的元素的從角標為0開始一個接著一個排列造成的。也就是說,從要插入的元素位置往後,每個元素都要向後移動一個位置。CPU快取友好的集合是基於陣列的。(其實也不是很友好,因為有時陣列會包含物件,這樣儲存的只是指向實際物件的指標)。 - LinkedList:
Deque
實現:每一個節點都儲存著上一個節點和下一個節點的指標。這就意味著資料的存取和更新具有線性複雜度(這也是一個最佳化的實現,每次操作都不會遍歷陣列一半以上,操作成本最高的元素就是陣列中間的那個)。如果想寫出高效的LinkedList
程式碼可以使用ListIterators
。如果你想用一個Queue/Deque
實現的話(你只需讀取第一個和最後一個元素就行了)——考慮用ArrayDeque
代替。 - Vector:一個帶有執行緒同步方法的
ArrayList
版本。現在直接用ArrayList
代替了。
Queues/deques
- ArrayDeque:
Deque
是基於有首尾指標的陣列(環形緩衝區)實現的。和LinkedList
不同,這個類沒有實現List
介面。因此,如果沒有首尾元素的話就不能取出任何元素。這個類比LinkedList
要好一些,因為它產生的垃圾數量較少(在擴充套件的時候舊的陣列會被丟棄)。 - Stack:一種後進先出的佇列。不要在生產程式碼中使用,使用別的
Deque
來代替(ArrayDeque
比較好)。 - PriorityQueue:一個基於優先順序的佇列。使用自然順序或者制定的比較器來排序。他的主要屬性——
poll/peek/remove/element
會返回一個佇列的最小值。不僅如此,PriorityQueue
還實現了Iterable
介面,佇列迭代時不進行排序(或者其他順序)。在需要排序的集合中,使用這個佇列會比TreeSet
等其他佇列要方便。
Maps
- HashMap:最常用的
Map
實現。只是將一個鍵和值相對應,並沒有其他的功能。對於複雜的hashCode method
,get/put
方法有固定的複雜度。 - EnumMap:列舉型別作為鍵值的
Map
。因為鍵的數量相對固定,所以在內部用一個陣列儲存對應值。通常來說,效率要高於HashMap
。 - HashTable:舊
HashMap
的同步版本,新的程式碼中也使用了HashMap
。 - IdentityHashMap:這是一個特殊的
Map
版本,它違背了一般Map
的規則:它使用 “==” 來比較引用而不是呼叫Object.equals
來判斷相等。這個特性使得此集合在遍歷圖表的演算法中非常實用——可以方便地在IdentityHashMap
中儲存處理過的節點以及相關的資料。 - LinkedHashMap :
HashMap
和LinkedList
的結合,所有元素的插入順序儲存在LinkedList
中。這就是為什麼迭代LinkedHashMap
的條目(entry)、鍵和值的時候總是遵循插入的順序。在JDK中,這是每元素消耗記憶體最大的集合。 - TreeMap:一種基於已排序且帶導向資訊Map的紅黑樹。每次插入都會按照自然順序或者給定的比較器排序。這個
Map
需要實現equals
方法和Comparable/Comparator
。compareTo
需要前後一致。這個類實現了一個NavigableMap
介面:可以帶有與鍵數量不同的入口,可以得到鍵的上一個或者下一個入口,可以得到另一Map
某一範圍的鍵(大致和SQL的BETWEEN
運算子相同),以及其他的一些方法。 - WeakHashMap:這種
Map
通常用在資料快取中。它將鍵儲存在WeakReference
中,就是說,如果沒有強引用指向鍵物件的話,這些鍵就可以被垃圾回收執行緒回收。值被儲存在強引用中。因此,你要確保沒有引用從值指向鍵或者將值也儲存在弱引用中m.put(key, new WeakReference(value))
。
Sets
- HashSet:一個基於
HashMap
的Set
實現。其中,所有的值為“假值”(同一個Object
物件具備和HashMap
同樣的效能。基於這個特性,這個資料結構會消耗更多不必要的記憶體。 - EnumSet:值為列舉型別的
Set
。Java的每一個enum
都對映成一個不同的int
。這就允許使用BitSet
——一個類似的集合結構,其中每一位元都對映成不同的enum
。EnumSet
有兩種實現,RegularEnumSet
——由一個單獨的long
儲存(能夠儲存64個列舉值,99.9%的情況下是夠用的),JumboEnumSet
——由long[]
儲存。 - BitSet:一個位元Set。需要時常考慮用
BitSet
處理一組密集的整數Set
(比如從一個預先知道的數字開始的id集合)。這個類用long[]
來儲存bit
。 - LinkedHashMap:與
HashSet
一樣,這個類基於LinkedHashMap
實現。這是唯一一個保持了插入順序的Set
。 - TreeSet:與
HashSet
類似。這個類是基於一個TreeMap
例項的。這是在單執行緒部分唯一一個排序的Set
。
java.util.Collections
就像有專門的java.util.Arrays
來處理陣列,Java中對集合也有java.util.Collections
來處理。
第一組方法主要返回集合的各種資料:
- Collections.checkedCollection / checkedList / checkedMap / checkedSet / checkedSortedMap / checkedSortedSet:檢查要新增的元素的型別並返回結果。任何嘗試新增非法型別的變數都會丟擲一個
ClassCastException
異常。這個功能可以防止在執行的時候出錯。//fixme - Collections.emptyList / emptyMap / emptySet :返回一個固定的空集合,不能新增任何元素。
- Collections.singleton / singletonList / singletonMap:返回一個只有一個入口的 set/list/map 集合。
- Collections.synchronizedCollection / synchronizedList / synchronizedMap / synchronizedSet / synchronizedSortedMap / synchronizedSortedSet:獲得集合的執行緒安全版本(多執行緒操作時開銷低但不高效,而且不支援類似
put
或update
這樣的複合操作) - Collections.unmodifiableCollection / unmodifiableList / unmodifiableMap / unmodifiableSet / unmodifiableSortedMap / unmodifiableSortedSet:返回一個不可變的集合。當一個不可變物件中包含集合的時候,可以使用此方法。
第二組方法中,其中有一些方法因為某些原因沒有加入到集合中:
- Collections.addAll:新增一些元素或者一個陣列的內容到集合中。
- Collections.binarySearch:和陣列的
Arrays.binarySearch
功能相同。 - Collections.disjoint:檢查兩個集合是不是沒有相同的元素。
- Collections.fill:用一個指定的值代替集合中的所有元素。
- Collections.frequency:集合中有多少元素是和給定元素相同的。
- Collections.indexOfSubList / lastIndexOfSubList:和
String.indexOf(String) / lastIndexOf(String)
方法類似——找出給定的List
中第一個出現或者最後一個出現的子表。 - Collections.max / min:找出基於自然順序或者比較器排序的集合中,最大的或者最小的元素。
- Collections.replaceAll:將集合中的某一元素替換成另一個元素。
- Collections.reverse:顛倒排列元素在集合中的順序。如果你要在排序之後使用這個方法的話,在列表排序時,最好使用
Collections.reverseOrder
比較器。 - Collections.rotate:根據給定的距離旋轉元素。
- Collections.shuffle:隨機排放
List
集合中的節點,可以給定你自己的生成器——例如java.util.Random / java.util.ThreadLocalRandom or java.security.SecureRandom
。 - Collections.sort:將集合按照自然順序或者給定的順序排序。
- Collections.swap:交換集合中兩個元素的位置(多數開發者都是自己實現這個操作的)。
併發集合
這一部分將介紹java.util.concurrent
包中執行緒安全的集合。這些集合的主要屬性是一個不可分割的必須執行的方法。因為併發的操作,例如add
或update
或者check
再update
,都有一次以上的呼叫,必須同步。因為第一步從集合中組合操作查詢到的資訊在開始第二步操作時可能變為無效資料。
多數的併發集合是在Java 1.5引入的。ConcurrentSkipListMap / ConcurrentSkipListSet
和 LinkedBlockingDeque
是在Java 1.6新加入的。Java 1.7加入了最後的 ConcurrentLinkedDeque
和 LinkedTransferQueue
Lists
- CopyOnWriteArrayList:list的實現每一次更新都會產生一個新的隱含陣列副本,所以這個操作成本很高。通常用在遍歷操作比更新操作多的集合,比如
listeners/observers
集合。
Queues/deques
- ArrayBlockingQueue:基於陣列實現的一個有界阻塞隊,大小不能重新定義。所以當你試圖向一個滿的佇列新增元素的時候,就會受到阻塞,直到另一個方法從佇列中取出元素。
- ConcurrentLinkedDeque / ConcurrentLinkedQueue:基於連結串列實現的無界佇列,新增元素不會堵塞。但是這就要求這個集合的消費者工作速度至少要和生產這一樣快,不然記憶體就會耗盡。嚴重依賴於CAS(compare-and-set)操作。
- DelayQueue:無界的儲存
Delayed
元素的集合。元素只有在延時已經過期的時候才能被取出。佇列的第一個元素延期最小(包含負值——延時已經過期)。當你要實現一個延期任務的佇列的時候使用(不要自己手動實現——使用ScheduledThreadPoolExecutor
)。 - LinkedBlockingDeque / LinkedBlockingQueue:可選擇有界或者無界基於連結串列的實現。在佇列為空或者滿的情況下使用
ReentrantLock-s
。 - LinkedTransferQueue:基於連結串列的無界佇列。除了通常的佇列操作,它還有一系列的
transfer
方法,可以讓生產者直接給等待的消費者傳遞資訊,這樣就不用將元素儲存到佇列中了。這是一個基於CAS操作的無鎖集合。 - PriorityBlockingQueue:
PriorityQueue
的無界的版本。 - SynchronousQueue:一個有界佇列,其中沒有任何記憶體容量。這就意味著任何插入操作必須等到響應的取出操作才能執行,反之亦反。如果不需要
Queue
介面的話,通過Exchanger
類也能完成響應的功能。
Maps
- ConcurrentHashMap:
get
操作全併發訪問,put
操作可配置併發操作的雜湊表。併發的級別可以通過建構函式中concurrencyLevel
引數設定(預設級別16)。該引數會在Map
內部劃分一些分割槽。在put
操作的時候只有只有更新的分割槽是鎖住的。這種Map
不是代替HashMap
的執行緒安全版本——任何get-then-put
的操作都需要在外部進行同步。 - ConcurrentSkipListMap:基於跳躍列表(Skip List)的
ConcurrentNavigableMap
實現。本質上這種集合可以當做一種TreeMap
的執行緒安全版本來使用。
Sets
- ConcurrentSkipListSet:使用
ConcurrentSkipListMap
來儲存的執行緒安全的Set
。 - CopyOnWriteArraySet:使用
CopyOnWriteArrayList
來儲存的執行緒安全的Set
。
相關閱讀
- Java 基本型別集合庫:Trove:Trove庫概述——儲存Java基本型別資料的集合庫(與大多數JDK中的
Objects
類不同)。 - Java常見資料型別記憶體佔用(1):各種類的記憶體佔用會所名,包括enums、EnumMap、EnumSet、BitSet、ArrayList、LinkedList和ArrayDeque。
- Java常見資料型別記憶體佔用(2):HashMap、HashSet、LinkedHashMap、LinkedHashSet、TreeMap、TreeSet和JDK 7 PriorityQueue記憶體佔用,以及對應的Trove替代類說明。
推薦閱讀
如果想要了解更多關於Java集合的知識,推薦閱讀以下書籍:
- Cay S. Horstmann. Core Java Volume I–Fundamentals (9th Edition) (Core Series) :第13章描述了Java集合.
- Naftalin, Wadler. Java Generics and Collections:書的第2部分(第10章至第17章)專門討論了Java集合。
- Goetz, Peierls, Bloch, Bowbeer, Holmes, Lea. Java Concurrency in Practice:最好的Java併發書籍。第5章討論了Java 1.5引入的併發集合。
總結
單執行緒 | 併發 | |
Lists |
|
|
Queues / deques |
|
|
Maps |
|
|
Sets |
|
|
相關文章
- Java集合詳解(一):全面理解Java集合Java
- Java整型資料型別(詳解)Java資料型別
- Java中的Type型別詳解Java型別
- Java集合詳解(二)Java
- Java集合詳解(三)Java
- Java集合類詳解Java
- 基礎-JAVA集合型別主要區別Java型別
- java基礎詳解-集合Java
- Java集合(三) ArrayList詳解Java
- Java集合(六) Set詳解Java
- Java集合(七) Queue詳解Java
- java集合(2)- java中HashMap詳解JavaHashMap
- Java 型別資訊詳解和反射機制Java型別反射
- 【Java集合】單列集合Collection常用方法詳解Java
- Java集合(四) LinkedList詳解Java
- Guava集合--新集合型別Guava型別
- [JAVA] Java 變數、表示式和資料型別詳解Java變數資料型別
- Java列舉型別enum的詳解及使用Java型別
- List型別集合型別
- Java 泛型詳解Java泛型
- Java泛型詳解Java泛型
- java集合學習(一):詳解ArrayListJava
- JAVA集合詳解(Collection和Map介面)Java
- Java中的併發集合詳解Java
- Java 集合詳解 | 一篇文章解決Java 三大集合Java
- C#型別詳解C#型別
- Java內功心法,Set集合的詳解Java
- Java集合詳解(二):ArrayList原理解析Java
- Java集合詳解(五):Hashtable原理解析Java
- Java集合詳解(三):HashMap原理解析JavaHashMap
- JavaScript 資料型別與型別判斷詳解JavaScript資料型別
- SAP 移動型別詳解型別
- JavaScript——資料型別詳解JavaScript資料型別
- MySQL 資料型別詳解MySQL 資料型別
- 詳解MySQL資料型別MySql資料型別
- oracle裡long型別詳解Oracle型別
- C++引用型別詳解C++型別
- MySQL 資料型別 詳解MySQL 資料型別