在Java開發中,會經常使用到集合,那么何為集合呢?
集合是用于存儲批量數據的容器工具,可以將其看做一個可變長度的數組。
在Java的API中集合被包含在java.util包中,被稱作collection框架。
Collection
我們先從Collection接口說起,接口Collection是collection層次結構的根接口,表示一組對象,一些collection是允許重復,一些則不可以。一些是有序的,而一些則是無序的。
所有通用的Collection實現類(通常通過它的一個子接口間接實現Collection)應該提供兩個“標準”構造方法:一個是 void(無參數)構造方法,用于創建空 collection;另一個是帶有Collection類型單參數的構造方法,用于創建一個具有與其參數相同元素新的 collection。實際上,后者允許用戶復制任何 collection,以生成所需實現類型的一個等效 collection。盡管無法強制執行此約定(因為接口不能包含構造方法),但是 Java 平臺庫中所有通用的Collection實現都遵從它。——官網介紹
下圖是常用Collection集合的簡化UML類圖:
Set接口
Set類型的集合存儲的元素是無序的且不可重復。
HashSet
對于HashSet,是基于HashMap(我們將會在后面介紹)實現的。HashSet底層采用HashMap來保存所有元素。主要用來做高性能的集運算,為快速查找而設計。存入HashSet的對象必須重寫hashCode()方法。
當我們向HashSet集合添加元素時,它是按照hash算法來存儲集合中的元素。首先HashSet會調用該對象的hashCode()方法來獲取到該對象的hashCode值,然后根據hashCode值決定該對象在Set集合中的存儲位置。HashSet集合判斷兩個元素相等的標準是兩個對象通過equals方法比較相等,同時兩個元素的hashCode()方法返回值也相等。因此如果兩個元素通過equals方法比較返回為true,但它們通過hashCode()方法返回的值的比較不相等,那么HashSet會將這兩個元素存儲在不同的位置,仍然能夠添加成功。
TreeSet
TreeSet是一種可指定排序方式的Set集合。其底層數據結構是二叉樹。通過比較兩元素的結果是否為0來保證元素的唯一性。
對于TreeSet的指定排序方式的方法有兩種:
方式一:讓元素自身具備比較性。只要讓元素實現Comparable接口,并覆蓋compareTo方法即可,當compareT方法的返回值為0時,就是相同元素。
方式二:讓集合自身具備比較性。該方式通過自定義比較器來實現,自定義類實現Comparator接口,覆蓋compare方法,將該Comparator接口實現類的對象作為參數傳遞給TreeSet的構造方法即可。
List接口
List類型的集合存儲的元素時有序且可重復。
ArrayList
ArrayList底層采用一個可變長度的數組實現,可存儲所有的元素,包括null。由于其實現了Serializable接口,所以其支持序列化。又由于其實現了RandomAccess接口,所以支持快速隨機訪問。ArrayList集合存儲的元素按照存入的先后順序排列元素,也就是說先進先出。
每個ArrayList實例都有一個容量,即用于存儲元素的數組的大小,這個容量可隨著不斷添加新元素而自動增加,。但是增長算法并沒有定義。當需要插入大量元素的時候,再插入前可以調用ensureCapacity方法來增加ArrayList的容量以提高插入效率。
ArrayList歲支持快速隨機訪問,但向集合中插入與移除元素的操作速度很慢。
LinkedList
LinkList在的底層是一個鏈表結構,因此LinkList對于集合增刪操作來說會更加占有優勢,只需要對指針(注:在Java中沒有指針的概念,這里是可以更形象地進行說明)操作即可。又因為鏈表的結構,LinkList可以被用作堆棧,隊列或雙向隊列。
Vector
Vector同ArrayList一樣底層是一個可變長度的數組。(下文有詳細的介紹)
Stack
Stack集合繼承于Vector類,Stack底層是一個堆棧結構,遵循后進先出(LIFO)原則,只能在一端執行插入操作(稱為入棧)或刪除操作(稱為出棧)
peek()//查看棧頂元素 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pop()//出棧,從棧頂移除一個元素 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? push(E Object)//入棧,將一個Object壓入棧頂 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? search(E Object)//查詢某個元素在棧中的位置
下圖是常用Map集合簡化的UML類圖
Map接口
Map類型的集合以鍵值對的形式存儲數據。不允許鍵的重復和從鍵到值的一對多映射。
HashMap
在HashMao內部通過一個哈希表來管理所有元素,同上文敘述的HashSet類似(其實應該說HashSet同HashMap類似),通過hashCode()方法和equals()方法來保證鍵的唯一性。也因此HashMap通過鍵的hashCode來快速的存取元素。
LinkedHashMap
LinkedHashMap繼承于HashMap,在底層是以哈希表和鏈接列表相結合而實現的.該集合類型保留插入順序,如果需要輸入順序與輸出順序相同,可以采用此集合。在LinkedHashMap內部有兩種排序方式,一種是插入順序(默認),一種是訪問順序(即近期訪問最少到近期訪問最多的順序),這兩種方式通過一個布爾值accessOrder進行切換,在LinkedHashMap的構造方法publicLinkedHashMap(intinitialCapacity,floatloadFactor,booleanaccessOrder)?{ ??super(initialCapacity,?loadFactor); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??this.accessOrder?=?accessOrder;}?中,如果accessOrder為true,表示按照訪問順序存儲元素,如果為false表示按照插入順序訪問元素。
Hashtable
同樣HashMap在底層是通過一個哈希表來實現的。
Properties
Properties繼承于Hashtable,該類表示一個持久的屬性值,可保存在流中或從流中加載。屬性列表中每個鍵及其對應值都是一個字符串。該類主要用于讀取Java中的配置文件,在Java中,其配置文件常為.properties文件,格式為文本文件,文件的內容的格式是“鍵=值”的格式,文本注釋信息可以用"#"來注釋。了解Properties的詳細用法,點我。
TreeMap
TreeMap實現了SortedMap接口,該集合根據鍵的自然順序進行排序,或根據創建集合時傳入的Comparator進行排序,具體取決于使用的構造方法。
各種集合的比較
1.同步性
以上介紹的集合中,除Vector、Stack、HashTable和Properties外,其他集合類型(HashSet、TreeSet、ArrayList、LinkedList、LinkedHashMap、TreeMap)都是非線程安全的。那么當多個線程同時訪問這些非線程的集合類型時,我們必須自己實現訪問同步,一種解決方案是在構造這些集合時構造一個同步集合,通過Collections類的靜態方法synchronizedCollection() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?synchronizedList() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?synchronizedMap() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?synchronizedSet() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?synchronizedSortedMap() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?synchronizedSortedSet()
2.ArrayList與Vector的比較
同步性方面,如1所述,Vector不需要自己實現同步方案,而ArrayList需要自己實現同步方案。ArrayList和Vector都是使用可變長數組來保存數據,當數組空間不足時,兩者就需要擴展其大小,ArrayList增加50%的大小,而Vector在默認情況下增長一倍的大小,但Vector可以自己設定這個增長幅度,在這方面Vector確實是有優勢的,根據情況設定增長幅度,對程序的性能來說會更加友好。
3.LinkedList與ArrayList、Vector的比較
這三種集合類型都支持快速隨機訪問,在集合的首或末對元素進行更重操作,三者并沒有什么的區別,但當從其他任意位置對元素進行操作,在性能上就會產生很大的差異。上文有敘述到LinkList是一個鏈表結構,在任意位置的對元素的操作都在一個常數級的時間,而Arrayl與Vecotr卻頗為費時。
4.HashMap和Hashtable的區別
Hashtable與HashMap類似,但Hashtable不允許鍵與值為null,HashMap卻允許。Hashtable是線程安全的,HashMap是縣城非安全的。相比較下,HashMap比Hashtable效率要高下,所以要根據具體情況選擇使用。。
5.TreeMap和HashMap的比較
針對于TreeMap,HashMap使用起來性能更好,因為在HashMap的構造方法中,可以自己調優初始容量和負載因子,這樣我們就可以根據具體的開發環境去優化我們的HashMap。TreeMap可以自定義順序遍歷元素,這更是與使用在需要具體排序的情況下。但是TreeMap的性能不是很好,TreeMap的get操作的時間復雜度是O(log(n)),針對于HashMap的O(1)差的還是比較多的。因此為了中和兩者,出現了LinkedHashMap。
6.各集合的性能比較
單線程模式下,測試元素在100~1000的情況下:
添加? HashMap效率最高,ArrayList最低,其他的效高的還有Stack、HashSet和Vector,較低的有LinkedList和TreeSet和TreeMap
刪除 HashMap效率最高,LinkedList最低,其他的HashSet、TreeMap和TreeSet效率較高,較低的有Vector、ArrayList和Stack
查找? HashMap效率最高,LinkedList最低,HashXXX和TreeXXX效率都比較高,而基于List類效率耗時是Map或Set的十倍左右。
多線程模式下,測試元素100~1000,線程數10的情況下:
添加 HashSet效率最高,LinkedList最低,HashXXX和TreeXXX效率都比較高,這里ArrayList效率較低,整體相差不大。
刪除 HashSet效率最高,LinkedList最低,整體性能同添加相似,但HashXXX或TreeXXX性能比List系列高出3倍。
查找 仍然是HashSet性能最好,LinkedList最低,性能較差的是ArrayList,其他的均表現很好。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ——摘自Adroid中文網
請根據具體情況具體選擇。
7.移動開發給出的建議
在谷歌官方推出的Android性能優化典范系列視頻中提到,Android為移動操作系統特意編寫了一些更加高效的容器,其中包括SparseArray和ArrayMap。
SparseArray采用二分法的方式存儲數據,所以SparseArray內的元素時按照鍵從小到大排列的,SparseArray只接受int類型的鍵
put(int key, E value)//增? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? append(int key, E value)//增,其中的key是大于所有現有的鍵的數組,以便于提升性能? ? ? ? ? ??? E get(int key)//查? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??????? E get(int key, E valueIfKeyNotFound)//查? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? delete(int key)//刪? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? remove(int key)//刪,實際上remove和delete是一樣的,remove方法中調用了delete方法? ? ? setValueAt(int index, E value)//改? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? put(int key, E value)//改
ArrayMap,該類是在API19中出現的,單位了兼容低版本support.v4中也有ArrayMap類。ArrayMap在內部使用兩個數組進行工作,其中一個數組記錄key hash過后的順序列表,另外一個數組按key的順序記錄Key-Value值。首先要清楚ArrayMap針對于移動設備的優化是在內存上進行的優化,也就是說使用這個容器比起HashMap會占用更少的內存,因為ArrayMap中的內存占用是連續不間斷的,但隨著數組中元素的增多,查找訪問單個對象的花費也會跟著增長,同時ArrayMap的插入與刪除的效率是不夠高的,但如果你需要的是在一百這個數量級上時,則完全不用擔心這些。
以上是自己對集合知識學習的總結,如果有不正確的地方,感謝指正,我也會及時修改。后面也會逐漸完善這方面的知識分享給大家。