Java各個集合(Collection)的特性和用途
這篇文章總結了所有的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集合的知識,推薦閱讀以下書籍:
總結
單執行緒 | 併發 | |
Lists |
|
|
Queues / deques |
|
|
Maps |
|
|
Sets |
|
|
相關推薦
Java各個集合(Collection)的特性和用途
這篇文章總結了所有的Java集合(Collection)。主要介紹各個集合的特性和用途,以及在不同的集合型別之間轉換的方式。 Arrays Array是Java特有的陣列。在你知道所要處理資料元素個數的情況下非常好用。java.util.Arrays 包含了許多處理資
Java--容器/集合類(Collection)理解和使用
、陣列和集合的比較 陣列:長度固定,用來存放基本型別的資料 集合:長度不固定,用來存放物件的引用 二、集合類的基本概念 1.java.util包中提供了一些集合類,這些集合類也被稱為容器。 常用的集合有List集合、Set集合、Map集合,他們的關係繼承如下:
JAVA--集合(Collection)
集合: ----------| Collection --------------| List 元素有序,可重複 -------------------| ArrayList 查詢速度快,增刪慢 -------------------| Linkedlist 增刪快,查詢慢
Java基礎——集合(一)——集合體系、Collection集合
一、集合概述 Java是一種面嚮物件語言,如果我們要針對多個物件進行操作,就必須對多個物件進行儲存。而陣列長度固定,不能滿足變化的要求。所以,java提供了集合。 特點 1. 長度可以發生改變
Java併發集合(二)-ConcurrentSkipListMap分析和使用
一、ConcurrentSkipListMap介紹 ConcurrentSkipListMap是執行緒安全的有序的雜湊表,適用於高併發的場景。ConcurrentSkipListMap和TreeMap,它們雖然都是有序的雜湊表。但是,第一,它們的執行緒安全機制不同,TreeMap是非執行緒安全的,而Concu
Java併發集合(三)-ConcurrentHashMap分析和使用
1 http://ifeve.com/hashmap-concurrenthashmap-%E7%9B%B8%E4%BF%A1%E7%9C%8B%E5%AE%8C%E8%BF%99%E7%AF%87%E6%B2%A1%E4%BA%BA%E8%83%BD%E9%9A%BE%E4%BD%8F%E4%BD%A0%E
JAVA基礎複習(四)異常和IO
1、不應該讓方法來終止程式,應該由呼叫者決定是否終止程式,但是又要儘量使用if來進行簡單測試而不是異常處理來測試 2、異常是物件,而物件都採用類來定義,異常的根類是java.lang.Throwable 3、異常類主要分為三種類型:系統錯誤(JAVA虛擬機器丟擲,免檢異常)、異常(要被被捕獲
Java併發程式設計(6):Runnable和Thread實現多執行緒的區別(含程式碼)
Java中實現多執行緒有兩種方法:繼承Thread類、實現Runnable介面,在程式開發中只要是多執行緒,肯定永遠以實現Runnable介面為主,因為實現Runnable介面相比繼承Thread類有如下優勢: 1、可以避免由於Java的單繼承特性而帶來的侷限; 2、增強程式的健壯性,程式碼能夠被多個執行
java學習筆記(二)parseInt和valueOf 以及字串+和StringBuilder的區別
parseInt和valueOf 我們平時應該都用過或者見過parseInt和valueOf這兩個方法。一般我們是想把String型別的字元數字轉成int型別。從這個功能層面來說,這兩個方法都一樣,都可以勝任這個功能。 但是,我們進入原始碼,看下Integer類下這兩個方法 pars
java學習筆記(一)int和Integer的區別
int和Integer的區別 1、Integer是int的包裝類,int則是java的一種基本資料型別 2、Integer變數必須例項化後才能使用,而int變數不需要 3、Integer實際是物件的引用,當new一個Integer時,實際上是生成一個指標指向此物件;而int則是直接儲存資料值
Java原始碼系列(1):Comparable和Comparator的區別
在將Comparable和Comparator區別之前,先補充一個知識點。 先看程式碼: public class Person<T> { private T id; public T getId() { retur
JAVA高階基礎(6)---Vector和LinkedList
Vector 是 List 介面一個長度可變的陣列的實現。 可以儲存null 是同步的(是執行緒安全的) 注:更多詳細方法請自行在 API 上查詢 ArrayList和Vector的區別:是否是同步的 package or
MongoDB 集合(Collection)對應的物理檔案
dbpath下是清一色的collection-n-***與index-n-***開頭的物理檔案,如何知道某一個集合與其對應與其對應的物理檔案? db.collection_name.stats() 返回的結果包含集合資料對應的物理檔案 db.collection_name
java中集合(二)
一、Map介面 1.Map介面是儲存一組成對出現的鍵(key)---- 值(value)物件。 2.Map介面中的key集無序,唯一,可以為空null,也就是隻有一個鍵集為空,如果有重複的後面的會覆蓋前面的。value集無序,允許重複。 3.Map介面得到常用方法
java輸入輸出(13) 字符集和CharSet
簡而言之,把看得懂字元轉換成看不懂的二進位制數就是編碼,將二進位制數轉換成看得懂的字元就是解碼 字符集其實是很簡單,沒有任何技術難度的,只是為了解決二進位制序列和字元之間的對應關係,需要一個大家都認同的字符集而已。 具體的講解穿插在程式碼中 import java.ni
Java內容梳理(3)識別符號和資料型別
識別符號 1、識別符號的命名規範 (1)是由26個英文字母(大小寫),數字,下劃線_,$組成; (2)識別符號僅不能以數字開頭,大小寫敏感,識別符號不能以關鍵字和保留字命名 2、程式設計中遇到的識別符號命名處理 (1)包名:全小寫,倒域名,如百度域名:baidu.c
java網路程式設計(2)URLDecoder和URLEncoder
一下為程式碼示例,具體的講解穿插在程式碼中 import java.net.URLDecoder; import java.net.URLEncoder; public class URLDecoderTest { public static void mai
Java內容梳理(8)this和super關鍵字
this關鍵字 1.表示當前物件 當前正在執行該方法的物件 2.表示呼叫本類構造方法 this(...) 注意:this(...)這種程式碼需要放在構造方法中的第一句 舉例: public Person() { //呼叫帶有兩個引數構造方法 th
Java提高班(六)反射和動態代理(JDK Proxy和Cglib)
反射和動態代理放有一定的相關性,但單純的說動態代理是由反射機制實現的,其實是不夠全面不準確的,動態代理是一種功能行為,而它的實現方法有很多。要怎麼理解以上這句話,請看下文。 一、反射 反射機制是 Java 語言提供的一種基礎功能,賦予程式在執行時自省(introspect,官方用語)的能力。通過反射我們可
java學習筆記(一)parseInt和valueOf 以及字串+和StringBuilder的區別
parseInt和valueOf 我們平時應該都用過或者見過parseInt和valueOf這兩個方法。一般我們是想把String型別的字元數字轉成int型別。從這個功能層面來說,這兩個方法都一樣,都可以勝任這個功能。 但是,我們進入原始碼,看下Integer類