Arraylist和Vector是采用數(shù)組方式存儲數(shù)據(jù)箕戳,此數(shù)組元素數(shù)大于實際存儲的數(shù)據(jù)以便增加插入元素某残,都允許直接序號索引元素国撵,但是插入數(shù)據(jù)要涉及到數(shù)組元素移動等內(nèi)存操作,所以插入數(shù)據(jù)慢玻墅,查找有下標(biāo)介牙,所以查詢數(shù)據(jù)快,Vector由于使用了synchronized方法-線程安全澳厢,所以性能上比ArrayList要差环础,LinkedList使用雙向鏈表實現(xiàn)存儲,按序號索引數(shù)據(jù)需要進(jìn)行向前或向后遍歷赏酥,但是插入數(shù)據(jù)時只需要記錄本項前后項即可喳整,插入數(shù)據(jù)較快谆构。
線性表裸扶,鏈表,哈希表是常用的數(shù)據(jù)結(jié)構(gòu)搬素,在進(jìn)行java開發(fā)時呵晨,JDK已經(jīng)為我們提供了一系列相應(yīng)的類實現(xiàn)基本的數(shù)據(jù)結(jié)構(gòu),這些結(jié)構(gòu)均在java.util包中熬尺,
collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
Collection接口
Collection是最基本的集合接口摸屠,一個Collection代表一組Object,即Collection的元素(elements)粱哼,一些Collection允許相同的元素而另一些不行季二。一些能排序而另一些不行。Java?SDK不提供直接繼承自Collection的類揭措,Java?SDK提供的類都是繼承自Collection的“子接口”如List和Set胯舷。
所有實現(xiàn)Collection接口的類都必須提供兩個標(biāo)準(zhǔn)的構(gòu)造函數(shù):無參數(shù)的構(gòu)造函數(shù)用于創(chuàng)建一個空的Collection,有一個Collection參數(shù)的構(gòu)造函數(shù)用于創(chuàng)建一個新的Collection绊含,這個新的Collection與傳入的Collection有相同的元素桑嘶。后一個構(gòu)造函數(shù)允許用戶復(fù)制一個Collection。
如何遍歷Collection中的每一個元素躬充?不論Collection的實際類型如何逃顶,它都支持一個iterator()的方法,該方法返回一個迭代子充甚,使用該迭代子即可逐一訪問Collection中每一個元素以政。典型的用法如下:
Iterator?it?=?collection.iterator();?//?獲得一個迭代子
while(it.hasNext())?{
Object?obj?=?it.next();?//?得到下一個元素
}
由Collection接口派生的兩個接口是List和Set。
List接口
List是有序的Collection伴找,使用此接口能夠精確的控制每個元素插入的位置盈蛮。用戶能夠使用索引(元素在List中的位置,類似于數(shù)組下標(biāo))來訪問List中的元素疆瑰,這類似于Java的數(shù)組眉反。
和下面要提到的Set不同昙啄,List允許有相同的元素。
除了具有Collection接口必備的iterator()方法外寸五,List還提供一個listIterator()方法梳凛,返回一個ListIterator接口,和標(biāo)準(zhǔn)的Iterator接口相比梳杏,ListIterator多了一些add()之類的方法韧拒,允許添加,刪除十性,設(shè)定元素叛溢,還能向前或向后遍歷。
實現(xiàn)List接口的常用類有LinkedList劲适,ArrayList楷掉,Vector和Stack。
ArrayList類
ArrayList實現(xiàn)了可變大小的數(shù)組霞势。它允許所有元素烹植,包括null。ArrayList沒有同步愕贡。
size草雕,isEmpty,get固以,set方法運(yùn)行時間為常數(shù)墩虹。但是add方法開銷為分?jǐn)偟某?shù),添加n個元素需要O(n)的時間憨琳。其他的方法運(yùn)行時間為線性诫钓。
每個ArrayList實例都有一個容量(Capacity),即用于存儲元素的數(shù)組的大小栽渴。這個容量可隨著不斷添加新元素而自動增加尖坤,但是增長算法并沒有定義。當(dāng)需要插入大量元素時闲擦,在插入前可以調(diào)用ensureCapacity方法來增加ArrayList的容量以提高插入效率慢味。
和LinkedList一樣,ArrayList也是非同步的(unsynchronized)墅冷。
Vector類
Vector非常類似ArrayList纯路,但是Vector是同步的。由Vector創(chuàng)建的Iterator寞忿,雖然和ArrayList創(chuàng)建的Iterator是同一接口驰唬,但是,因為Vector是同步的,當(dāng)一個Iterator被創(chuàng)建而且正在被使用叫编,另一個線程改變了Vector的狀態(tài)(例如辖佣,添加或刪除了一些元素),這時調(diào)用Iterator的方法時將拋出ConcurrentModificationException搓逾,因此必須捕獲該異常卷谈。
Stack?類
Stack繼承自Vector,實現(xiàn)一個后進(jìn)先出的堆棧霞篡。Stack提供5個額外的方法使得Vector得以被當(dāng)作堆棧使用世蔗。基本的push和pop方法朗兵,還有peek方法得到棧頂?shù)脑匚哿埽琫mpty方法測試堆棧是否為空,search方法檢測一個元素在堆棧中的位置余掖。Stack剛創(chuàng)建后是空棧寸爆。
Map接口
請注意,Map沒有繼承Collection接口浊吏,Map提供key到value的映射而昨。一個Map中不能包含相同的key救氯,每個key只能映射一個value找田。Map接口提供3種集合的視圖,Map的內(nèi)容可以被當(dāng)作一組key集合着憨,一組value集合墩衙,或者一組key-value映射。
Hashtable類
Hashtable繼承Map接口甲抖,實現(xiàn)一個key-value映射的哈希表漆改。任何非空(non-null)的對象都可作為key或者value。
添加數(shù)據(jù)使用put(key,?value)准谚,取出數(shù)據(jù)使用get(key)挫剑,這兩個基本操作的時間開銷為常數(shù)。
Hashtable通過initial?capacity和load?factor兩個參數(shù)調(diào)整性能柱衔。通常缺省的load?factor?0.75較好地實現(xiàn)了時間和空間的均衡樊破。增大load?factor可以節(jié)省空間但相應(yīng)的查找時間將增大,這會影響像get和put這樣的操作唆铐。
使用Hashtable的簡單示例如下哲戚,將1,2艾岂,3放到Hashtable中顺少,他們的key分別是”one”,”two”,”three”:
Hashtablenumbers?=?newHashtable();
numbers.put(“one”,?new?Integer(1));
numbers.put(“two”,?new?Integer(2));
numbers.put(“three”,?new?Integer(3));
要取出一個數(shù)脆炎,比如2梅猿,用相應(yīng)的key:
Integer?n?=?(Integer)numbers.get(“two”);
System.out.println(“two?=?”?+?n);
由于作為key的對象將通過計算其散列函數(shù)來確定與之對應(yīng)的value的位置,因此任何作為key的對象都必須實現(xiàn)hashCode和equals方法秒裕。hashCode和equals方法繼承自根類Object粒没,如果你用自定義的類當(dāng)作key的話,要相當(dāng)小心簇爆,按照散列函數(shù)的定義癞松,如果兩個對象相同,即obj1.equals(obj2)=true入蛆,則它們的hashCode必須相同响蓉,但如果兩個對象不同,則它們的hashCode不一定不同哨毁,如果兩個不同對象的hashCode相同枫甲,這種現(xiàn)象稱為沖突,沖突會導(dǎo)致操作哈希表的時間開銷增大扼褪,所以盡量定義好的hashCode()方法想幻,能加快哈希表的操作。
如果相同的對象有不同的hashCode话浇,對哈希表的操作會出現(xiàn)意想不到的結(jié)果(期待的get方法返回null)脏毯,要避免這種問題,只需要牢記一條:要同時復(fù)寫equals方法和hashCode方法幔崖,而不要只寫其中一個食店。
Hashtable是同步的。
HashMap類
HashMap和Hashtable類似赏寇,不同之處在于HashMap是非同步的吉嫩,并且允許null,即null?value和null?key嗅定。自娩,但是將HashMap視為Collection時(values()方法可返回Collection),其迭代子操作時間開銷和HashMap的容量成比例渠退。因此忙迁,如果迭代操作的性能相當(dāng)重要的話,不要將HashMap的初始化容量設(shè)得過高智什,或者load?factor過低动漾。
WeakHashMap類
WeakHashMap是一種改進(jìn)的HashMap,它對key實行“弱引用”荠锭,如果一個key不再被外部所引用旱眯,那么該key可以被GC回收。
總結(jié)
如果涉及到堆棧,隊列等操作删豺,應(yīng)該考慮用List共虑,對于需要快速插入,刪除元素呀页,應(yīng)該使用LinkedList妈拌,如果需要快速隨機(jī)訪問元素,應(yīng)該使用ArrayList蓬蝶。
如果程序在單線程環(huán)境中尘分,或者訪問僅僅在一個線程中進(jìn)行,考慮非同步的類丸氛,其效率較高培愁,如果多個線程可能同時操作一個類,應(yīng)該使用同步的類缓窜。
要特別注意對哈希表的操作定续,作為key的對象要正確復(fù)寫equals和hashCode方法。
盡量返回接口而非實際的類型禾锤,如返回List而非ArrayList私股,這樣如果以后需要將ArrayList換成LinkedList時,客戶端代碼不用改變恩掷。這就是針對抽象編程倡鲸。
同步性
Vector是同步的。這個類中的一些方法保證了Vector中的對象是線程安全的螃成。而ArrayList則是異步的旦签,因此ArrayList中的對象并不是線程安全的。因為同步的要求會影響執(zhí)行的效率寸宏,所以如果你不需要線程安全的集合那么使用ArrayList是一個很好的選擇,這樣可以避免由于同步帶來的不必要的性能開銷偿曙。
數(shù)據(jù)增長
從內(nèi)部實現(xiàn)機(jī)制來講ArrayList和Vector都是使用數(shù)組(Array)來控制集合中的對象氮凝。當(dāng)你向這兩種類型中增加元素的時候,如果元素的數(shù)目超出了內(nèi)部數(shù)組目前的長度它們都需要擴(kuò)展內(nèi)部數(shù)組的長度望忆,Vector缺省情況下自動增長原來一倍的數(shù)組長度罩阵,ArrayList是原來的50%,所以最后你獲得的這個集合所占的空間總是比你實際需要的要大。所以如果你要在集合中保存大量的數(shù)據(jù)那么使用Vector有一些優(yōu)勢启摄,因為你可以通過設(shè)置集合的初始化大小來避免不必要的資源開銷稿壁。
使用模式
在ArrayList和Vector中,從一個指定的位置(通過索引)查找數(shù)據(jù)或是在集合的末尾增加歉备、移除一個元素所花費(fèi)的時間是一樣的傅是,這個時間我們用O(1)表示。但是,如果在集合的其他位置增加或移除元素那么花費(fèi)的時間會呈線形增長:O(n-i)喧笔,其中n代表集合中元素的個數(shù)帽驯,i代表元素增加或移除元素的索引位置。為什么會這樣呢书闸?以為在進(jìn)行上述操作的時候集合中第i和第i個元素之后的所有元素都要執(zhí)行位移的操作尼变。這一切意味著什么呢?
這意味著浆劲,你只是查找特定位置的元素或只在集合的末端增加嫌术、移除元素,那么使用Vector或ArrayList都可以牌借。如果是其他操作蛉威,你最好選擇其他的集合操作類。比如走哺,LinkList集合類在增加或移除集合中任何位置的元素所花費(fèi)的時間都是一樣的?O(1)蚯嫌,但它在索引一個元素的使用缺比較慢-O(i),其中i是索引的位置.使用ArrayList也很容易,因為你可以簡單的使用索引來代替創(chuàng)建iterator對象的操作丙躏。LinkList也會為每個插入的元素創(chuàng)建對象择示,所有你要明白它也會帶來額外的開銷。
最后晒旅,在《Practical?Java》一書中Peter?Haggar建議使用一個簡單的數(shù)組(Array)來代替Vector或ArrayList栅盲。尤其是對于執(zhí)行效率要求高的程序更應(yīng)如此。因為使用數(shù)組(Array)避免了同步废恋、額外的方法調(diào)用和不必要的重新分配空間的操作谈秫。