在Java開發(fā)中,會經(jīng)常使用到集合,那么何為集合呢?
集合是用于存儲批量數(shù)據(jù)的容器工具阴绢,可以將其看做一個(gè)可變長度的數(shù)組。
在Java的API中集合被包含在java.util包中艰躺,被稱作collection框架呻袭。
Collection
我們先從Collection接口說起,接口Collection是collection層次結(jié)構(gòu)的根接口腺兴,表示一組對象左电,一些collection是允許重復(fù),一些則不可以含长。一些是有序的券腔,而一些則是無序的伏穆。
所有通用的Collection實(shí)現(xiàn)類(通常通過它的一個(gè)子接口間接實(shí)現(xiàn)Collection)應(yīng)該提供兩個(gè)“標(biāo)準(zhǔn)”構(gòu)造方法:一個(gè)是 void(無參數(shù))構(gòu)造方法拘泞,用于創(chuàng)建空 collection;另一個(gè)是帶有Collection類型單參數(shù)的構(gòu)造方法枕扫,用于創(chuàng)建一個(gè)具有與其參數(shù)相同元素新的 collection陪腌。實(shí)際上,后者允許用戶復(fù)制任何 collection烟瞧,以生成所需實(shí)現(xiàn)類型的一個(gè)等效 collection诗鸭。盡管無法強(qiáng)制執(zhí)行此約定(因?yàn)榻涌诓荒馨瑯?gòu)造方法),但是 Java 平臺庫中所有通用的Collection實(shí)現(xiàn)都遵從它参滴∏堪叮——官網(wǎng)介紹
下圖是常用Collection集合的簡化UML類圖:
Set接口
Set類型的集合存儲的元素是無序的且不可重復(fù)。
HashSet
對于HashSet砾赔,是基于HashMap(我們將會在后面介紹)實(shí)現(xiàn)的蝌箍。HashSet底層采用HashMap來保存所有元素。主要用來做高性能的集運(yùn)算暴心,為快速查找而設(shè)計(jì)妓盲。存入HashSet的對象必須重寫hashCode()方法。
當(dāng)我們向HashSet集合添加元素時(shí)专普,它是按照hash算法來存儲集合中的元素悯衬。首先HashSet會調(diào)用該對象的hashCode()方法來獲取到該對象的hashCode值,然后根據(jù)hashCode值決定該對象在Set集合中的存儲位置檀夹。HashSet集合判斷兩個(gè)元素相等的標(biāo)準(zhǔn)是兩個(gè)對象通過equals方法比較相等筋粗,同時(shí)兩個(gè)元素的hashCode()方法返回值也相等策橘。因此如果兩個(gè)元素通過equals方法比較返回為true,但它們通過hashCode()方法返回的值的比較不相等娜亿,那么HashSet會將這兩個(gè)元素存儲在不同的位置役纹,仍然能夠添加成功。
TreeSet
TreeSet是一種可指定排序方式的Set集合暇唾。其底層數(shù)據(jù)結(jié)構(gòu)是二叉樹促脉。通過比較兩元素的結(jié)果是否為0來保證元素的唯一性。
對于TreeSet的指定排序方式的方法有兩種:
方式一:讓元素自身具備比較性策州。只要讓元素實(shí)現(xiàn)Comparable接口瘸味,并覆蓋compareTo方法即可馍驯,當(dāng)compareT方法的返回值為0時(shí)帆锋,就是相同元素。
方式二:讓集合自身具備比較性嫡秕。該方式通過自定義比較器來實(shí)現(xiàn)孽糖,自定義類實(shí)現(xiàn)Comparator接口枯冈,覆蓋compare方法,將該Comparator接口實(shí)現(xiàn)類的對象作為參數(shù)傳遞給TreeSet的構(gòu)造方法即可办悟。
List接口
List類型的集合存儲的元素時(shí)有序且可重復(fù)尘奏。
ArrayList
ArrayList底層采用一個(gè)可變長度的數(shù)組實(shí)現(xiàn),可存儲所有的元素病蛉,包括null炫加。由于其實(shí)現(xiàn)了Serializable接口,所以其支持序列化铺然。又由于其實(shí)現(xiàn)了RandomAccess接口俗孝,所以支持快速隨機(jī)訪問。ArrayList集合存儲的元素按照存入的先后順序排列元素魄健,也就是說先進(jìn)先出赋铝。
每個(gè)ArrayList實(shí)例都有一個(gè)容量,即用于存儲元素的數(shù)組的大小沽瘦,這個(gè)容量可隨著不斷添加新元素而自動(dòng)增加革骨,。但是增長算法并沒有定義其垄。當(dāng)需要插入大量元素的時(shí)候苛蒲,再插入前可以調(diào)用ensureCapacity方法來增加ArrayList的容量以提高插入效率。
ArrayList歲支持快速隨機(jī)訪問绿满,但向集合中插入與移除元素的操作速度很慢臂外。
LinkedList
LinkList在的底層是一個(gè)鏈表結(jié)構(gòu),因此LinkList對于集合增刪操作來說會更加占有優(yōu)勢,只需要對指針(注:在Java中沒有指針的概念漏健,這里是可以更形象地進(jìn)行說明)操作即可嚎货。又因?yàn)殒湵淼慕Y(jié)構(gòu),LinkList可以被用作堆棧蔫浆,隊(duì)列或雙向隊(duì)列殖属。
Vector
Vector同ArrayList一樣底層是一個(gè)可變長度的數(shù)組。(下文有詳細(xì)的介紹)
Stack
Stack集合繼承于Vector類瓦盛,Stack底層是一個(gè)堆棧結(jié)構(gòu)洗显,遵循后進(jìn)先出(LIFO)原則,只能在一端執(zhí)行插入操作(稱為入棧)或刪除操作(稱為出棧)
peek()//查看棧頂元素 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pop()//出棧原环,從棧頂移除一個(gè)元素 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? push(E Object)//入棧挠唆,將一個(gè)Object壓入棧頂 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? search(E Object)//查詢某個(gè)元素在棧中的位置
下圖是常用Map集合簡化的UML類圖
Map接口
Map類型的集合以鍵值對的形式存儲數(shù)據(jù)。不允許鍵的重復(fù)和從鍵到值的一對多映射嘱吗。
HashMap
在HashMao內(nèi)部通過一個(gè)哈希表來管理所有元素玄组,同上文敘述的HashSet類似(其實(shí)應(yīng)該說HashSet同HashMap類似),通過hashCode()方法和equals()方法來保證鍵的唯一性谒麦。也因此HashMap通過鍵的hashCode來快速的存取元素俄讹。
LinkedHashMap
LinkedHashMap繼承于HashMap,在底層是以哈希表和鏈接列表相結(jié)合而實(shí)現(xiàn)的.該集合類型保留插入順序绕德,如果需要輸入順序與輸出順序相同患膛,可以采用此集合。在LinkedHashMap內(nèi)部有兩種排序方式迁匠,一種是插入順序(默認(rèn))剩瓶,一種是訪問順序(即近期訪問最少到近期訪問最多的順序)驹溃,這兩種方式通過一個(gè)布爾值accessOrder進(jìn)行切換城丧,在LinkedHashMap的構(gòu)造方法publicLinkedHashMap(intinitialCapacity,floatloadFactor,booleanaccessOrder)?{ ??super(initialCapacity,?loadFactor); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??this.accessOrder?=?accessOrder;}?中,如果accessOrder為true豌鹤,表示按照訪問順序存儲元素亡哄,如果為false表示按照插入順序訪問元素。
Hashtable
同樣HashMap在底層是通過一個(gè)哈希表來實(shí)現(xiàn)的布疙。
Properties
Properties繼承于Hashtable蚊惯,該類表示一個(gè)持久的屬性值,可保存在流中或從流中加載灵临。屬性列表中每個(gè)鍵及其對應(yīng)值都是一個(gè)字符串截型。該類主要用于讀取Java中的配置文件,在Java中儒溉,其配置文件常為.properties文件宦焦,格式為文本文件,文件的內(nèi)容的格式是“鍵=值”的格式,文本注釋信息可以用"#"來注釋波闹。了解Properties的詳細(xì)用法酝豪,點(diǎn)我。
TreeMap
TreeMap實(shí)現(xiàn)了SortedMap接口精堕,該集合根據(jù)鍵的自然順序進(jìn)行排序孵淘,或根據(jù)創(chuàng)建集合時(shí)傳入的Comparator進(jìn)行排序,具體取決于使用的構(gòu)造方法歹篓。
各種集合的比較
1.同步性
以上介紹的集合中瘫证,除Vector、Stack庄撮、HashTable和Properties外痛悯,其他集合類型(HashSet、TreeSet重窟、ArrayList载萌、LinkedList、LinkedHashMap巡扇、TreeMap)都是非線程安全的扭仁。那么當(dāng)多個(gè)線程同時(shí)訪問這些非線程的集合類型時(shí),我們必須自己實(shí)現(xiàn)訪問同步厅翔,一種解決方案是在構(gòu)造這些集合時(shí)構(gòu)造一個(gè)同步集合乖坠,通過Collections類的靜態(tài)方法synchronizedCollection() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?synchronizedList() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?synchronizedMap() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?synchronizedSet() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?synchronizedSortedMap() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?synchronizedSortedSet()
2.ArrayList與Vector的比較
同步性方面,如1所述刀闷,Vector不需要自己實(shí)現(xiàn)同步方案熊泵,而ArrayList需要自己實(shí)現(xiàn)同步方案。ArrayList和Vector都是使用可變長數(shù)組來保存數(shù)據(jù)甸昏,當(dāng)數(shù)組空間不足時(shí)顽分,兩者就需要擴(kuò)展其大小,ArrayList增加50%的大小施蜜,而Vector在默認(rèn)情況下增長一倍的大小卒蘸,但Vector可以自己設(shè)定這個(gè)增長幅度,在這方面Vector確實(shí)是有優(yōu)勢的翻默,根據(jù)情況設(shè)定增長幅度缸沃,對程序的性能來說會更加友好。
3.LinkedList與ArrayList修械、Vector的比較
這三種集合類型都支持快速隨機(jī)訪問趾牧,在集合的首或末對元素進(jìn)行更重操作,三者并沒有什么的區(qū)別肯污,但當(dāng)從其他任意位置對元素進(jìn)行操作翘单,在性能上就會產(chǎn)生很大的差異梯皿。上文有敘述到LinkList是一個(gè)鏈表結(jié)構(gòu),在任意位置的對元素的操作都在一個(gè)常數(shù)級的時(shí)間县恕,而Arrayl與Vecotr卻頗為費(fèi)時(shí)东羹。
4.HashMap和Hashtable的區(qū)別
Hashtable與HashMap類似,但Hashtable不允許鍵與值為null忠烛,HashMap卻允許属提。Hashtable是線程安全的,HashMap是縣城非安全的美尸。相比較下冤议,HashMap比Hashtable效率要高下,所以要根據(jù)具體情況選擇使用师坎。恕酸。
5.TreeMap和HashMap的比較
針對于TreeMap,HashMap使用起來性能更好胯陋,因?yàn)樵贖ashMap的構(gòu)造方法中蕊温,可以自己調(diào)優(yōu)初始容量和負(fù)載因子,這樣我們就可以根據(jù)具體的開發(fā)環(huán)境去優(yōu)化我們的HashMap遏乔。TreeMap可以自定義順序遍歷元素义矛,這更是與使用在需要具體排序的情況下。但是TreeMap的性能不是很好盟萨,TreeMap的get操作的時(shí)間復(fù)雜度是O(log(n))凉翻,針對于HashMap的O(1)差的還是比較多的。因此為了中和兩者捻激,出現(xiàn)了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類效率耗時(shí)是Map或Set的十倍左右鞭达。
多線程模式下,測試元素100~1000,線程數(shù)10的情況下:
添加 HashSet效率最高畴蹭,LinkedList最低坦仍,HashXXX和TreeXXX效率都比較高,這里ArrayList效率較低叨襟,整體相差不大繁扎。
刪除 HashSet效率最高,LinkedList最低糊闽,整體性能同添加相似梳玫,但HashXXX或TreeXXX性能比List系列高出3倍。
查找 仍然是HashSet性能最好右犹,LinkedList最低提澎,性能較差的是ArrayList,其他的均表現(xiàn)很好念链。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ——摘自Adroid中文網(wǎng)
請根據(jù)具體情況具體選擇盼忌。
7.移動(dòng)開發(fā)給出的建議
在谷歌官方推出的Android性能優(yōu)化典范系列視頻中提到,Android為移動(dòng)操作系統(tǒng)特意編寫了一些更加高效的容器掂墓,其中包括SparseArray和ArrayMap碴犬。
SparseArray采用二分法的方式存儲數(shù)據(jù),所以SparseArray內(nèi)的元素時(shí)按照鍵從小到大排列的梆暮,SparseArray只接受int類型的鍵
put(int key, E value)//增? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? append(int key, E value)//增服协,其中的key是大于所有現(xiàn)有的鍵的數(shù)組,以便于提升性能? ? ? ? ? ??? E get(int key)//查? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??????? E get(int key, E valueIfKeyNotFound)//查? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? delete(int key)//刪? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? remove(int key)//刪啦粹,實(shí)際上remove和delete是一樣的偿荷,remove方法中調(diào)用了delete方法? ? ? setValueAt(int index, E value)//改? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? put(int key, E value)//改
ArrayMap,該類是在API19中出現(xiàn)的唠椭,單位了兼容低版本support.v4中也有ArrayMap類跳纳。ArrayMap在內(nèi)部使用兩個(gè)數(shù)組進(jìn)行工作,其中一個(gè)數(shù)組記錄key hash過后的順序列表贪嫂,另外一個(gè)數(shù)組按key的順序記錄Key-Value值寺庄。首先要清楚ArrayMap針對于移動(dòng)設(shè)備的優(yōu)化是在內(nèi)存上進(jìn)行的優(yōu)化,也就是說使用這個(gè)容器比起HashMap會占用更少的內(nèi)存力崇,因?yàn)锳rrayMap中的內(nèi)存占用是連續(xù)不間斷的斗塘,但隨著數(shù)組中元素的增多,查找訪問單個(gè)對象的花費(fèi)也會跟著增長亮靴,同時(shí)ArrayMap的插入與刪除的效率是不夠高的馍盟,但如果你需要的是在一百這個(gè)數(shù)量級上時(shí),則完全不用擔(dān)心這些茧吊。
以上是自己對集合知識學(xué)習(xí)的總結(jié)贞岭,如果有不正確的地方八毯,感謝指正,我也會及時(shí)修改瞄桨。后面也會逐漸完善這方面的知識分享給大家话速。