Java集合詳解【面試+提高】 在說(shuō)集合前我們不得不說(shuō)一下數(shù)組 存放一組相同的數(shù)據(jù)類(lèi)型(基本或?qū)ο?的數(shù)據(jù),從而實(shí)現(xiàn)對(duì)數(shù)據(jù)的管理 一、數(shù)組和集合的比較 數(shù)組不是面向?qū)ο蟮?,存在明顯的缺陷,集合彌補(bǔ)了數(shù)組的缺點(diǎn),比數(shù)組更靈活更實(shí)用,而且不同的集合框架類(lèi)可適用不同場(chǎng)合。如下: 二、Java集合 此圖可用Windows系統(tǒng)自帶畫(huà)圖工具查看比較清晰 Collection和Map,是集合框架的根接口。 Collection的子接口: Set:接口 ---實(shí)現(xiàn)類(lèi): HashSet、LinkedHashSet List集合 有序列表,允許存放重復(fù)的元素; ArrayList 底層是Object數(shù)組,所以ArrayList具有數(shù)組的查詢(xún)速度快的優(yōu)點(diǎn)以及增刪速度慢的缺點(diǎn)。 而在LinkedList的底層是一種雙向循環(huán)鏈表。在此鏈表上每一個(gè)數(shù)據(jù)節(jié)點(diǎn)都由三部分組成:前指針(指向前面的節(jié)點(diǎn)的位置),數(shù)據(jù),后指針(指向后面的節(jié)點(diǎn)的位置)。最后一個(gè)節(jié)點(diǎn)的后指針指向第一個(gè)節(jié)點(diǎn)的前指針,形成一個(gè)循環(huán)。 雙向循環(huán)鏈表的查詢(xún)效率低但是增刪效率高。 ArrayList和LinkedList在用法上沒(méi)有區(qū)別,但是在功能上還是有區(qū)別的。 LinkedList LinkedList是采用雙向循環(huán)鏈表實(shí)現(xiàn)的。 經(jīng)常用在增刪操作較多而查詢(xún)操作很少的情況下: 隊(duì)列和堆棧。 隊(duì)列:先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)。 棧:后進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)。 注意:使用棧的時(shí)候一定不能提供方法讓不是最后一個(gè)元素的元素獲得出棧的機(jī)會(huì)。 Vector (與ArrayList相似,區(qū)別是Vector是重量級(jí)的組件,使用使消耗的資源比較多。) 結(jié)論:在考慮并發(fā)的情況下用Vector(保證線程的安全)。 在不考慮并發(fā)的情況下用ArrayList(不能保證線程的安全)。 ArrayList自動(dòng)擴(kuò)充機(jī)制
List常用方法: Set集合 擴(kuò)展Collection接口 HashSet 的后臺(tái)有一個(gè)HashMap;初始化后臺(tái)容量;只不過(guò)生成一個(gè)HashSet的話,系統(tǒng)只提供key的訪問(wèn); 如果有兩個(gè)Key重復(fù),那么會(huì)覆蓋之前的; HashSet類(lèi)HashSet類(lèi)直接實(shí)現(xiàn)了Set接口, 其底層其實(shí)是包裝了一個(gè)HashMap去實(shí)現(xiàn)的。HashSet采用HashCode算法來(lái)存取集合中的元素,因此具有比較好的讀取和查找性能。 HashSet的特征
HashSet的equals和HashCode前面說(shuō)過(guò),Set集合是不允許重復(fù)元素的,否則將會(huì)引發(fā)各種奇怪的問(wèn)題。那么HashSet如何判斷元素重復(fù)呢? HashSet需要同時(shí)通過(guò)equals和HashCode來(lái)判斷兩個(gè)元素是否相等,具體規(guī)則是,如果兩個(gè)元素通過(guò)equals為true,并且兩個(gè)元素的hashCode相等,則這兩個(gè)元素相等(即重復(fù))。 所以如果要重寫(xiě)保存在HashSet中的對(duì)象的equals方法,也要重寫(xiě)hashCode方法,重寫(xiě)前后hashCode返回的結(jié)果相等(即保證保存在同一個(gè)位置)。所有參與計(jì)算 hashCode() 返回值的關(guān)鍵屬性,都應(yīng)該用于作為 equals() 比較的標(biāo)準(zhǔn)。 試想如果重寫(xiě)了equals方法但不重寫(xiě)hashCode方法,即相同equals結(jié)果的兩個(gè)對(duì)象將會(huì)被HashSet當(dāng)作兩個(gè)元素保存起來(lái),這與我們?cè)O(shè)計(jì)HashSet的初衷不符(元素不重復(fù))。 另外如果兩個(gè)元素哈市Code相等但equals結(jié)果不為true,HashSet會(huì)將這兩個(gè)元素保存在同一個(gè)位置,并將超過(guò)一個(gè)的元素以鏈表方式保存,這將影響HashSet的效率。 如果重寫(xiě)了equals方法但沒(méi)有重寫(xiě)hashCode方法,則HashSet可能無(wú)法正常工作,比如下面的例子。 上面注釋了hashCode方法,所以你將會(huì)看到下面的結(jié)果。 false [R[count:9 # hashCode:14927396], R[count:5 # hashCode:24417480], R[count:-2 # hashCode:31817359], R[count:-3 # hashCode:13884241]] 取消注釋?zhuān)瑒t結(jié)果就正確了 copy true [R[count:5 # hashCode:5], R[count:9 # hashCode:9], R[count:-3 # hashCode:-3], R[count:-2 # hashCode:-2]]
LinkedHashSet的特征 LinkedHashSet是HashSet的一個(gè)子類(lèi),LinkedHashSet也根據(jù)HashCode的值來(lái)決定元素的存儲(chǔ)位置,但同時(shí)它還用一個(gè)鏈表來(lái)維護(hù)元素的插入順序,插入的時(shí)候即要計(jì)算hashCode又要維護(hù)鏈表,而遍歷的時(shí)候只需要按鏈表來(lái)訪問(wèn)元素。查看LinkedHashSet的源碼發(fā)現(xiàn)它是樣的, 在JAVA7中, LinkedHashSet沒(méi)有定義任何方法,只有四個(gè)構(gòu)造函數(shù),它的構(gòu)造函數(shù)調(diào)用了父類(lèi)(HashSet)的帶三個(gè)參數(shù)的構(gòu)造方法,父類(lèi)的構(gòu)造函數(shù)如下, 由此可知,LinkedHashSet本質(zhì)上也是從LinkedHashMap而來(lái),LinkedHashSet的所有方法都繼承自HashSet, 而它能維持元素的插入順序的性質(zhì)則繼承自LinkedHashMap. 下面是一個(gè)LinkedHashSet維持元素插入順序的例子, 輸入如下 TreeSet類(lèi)的特征TreeSet實(shí)現(xiàn)了SortedSet接口,顧名思義這是一種排序的Set集合,查看jdk源碼發(fā)現(xiàn)底層是用TreeMap實(shí)現(xiàn)的,本質(zhì)上是一個(gè)紅黑樹(shù)原理。 正因?yàn)樗桥判蛄说?,所以相?duì)HashSet來(lái)說(shuō),TreeSet提供了一些額外的按排序位置訪問(wèn)元素的方法,例如first(), last(), lower(), higher(), subSet(), headSet(), tailSet(). TreeSet的排序分兩種類(lèi)型,一種是自然排序,另一種是定制排序。 自然排序(在元素中寫(xiě)排序規(guī)則)TreeSet 會(huì)調(diào)用compareTo方法比較元素大小,然后按升序排序。所以自然排序中的元素對(duì)象,都必須實(shí)現(xiàn)了Comparable接口,否則會(huì)跑出異常。對(duì)于TreeSet判斷元素是否重復(fù)的標(biāo)準(zhǔn),也是調(diào)用元素從Comparable接口繼承而來(lái)額compareTo方法,如果返回0則是重復(fù)元素(兩個(gè)元素I相等)。Java的常見(jiàn)類(lèi)都已經(jīng)實(shí)現(xiàn)了Comparable接口,下面舉例說(shuō)明沒(méi)有實(shí)現(xiàn)Comparable存入TreeSet時(shí)引發(fā)異常的情況。 運(yùn)行程序會(huì)拋出如下異常 將上面的Err類(lèi)實(shí)現(xiàn)Comparable接口之后程序就能正常運(yùn)行了 還有個(gè)重要問(wèn)題是,因?yàn)門(mén)reeSet會(huì)調(diào)用元素的compareTo方法,這就要求所有元素的類(lèi)型都相同,否則也會(huì)發(fā)生異常。也就是說(shuō),TreeSet只允許存入同一類(lèi)的元素。例如下面這個(gè)例子就會(huì)拋出類(lèi)型轉(zhuǎn)換異常 運(yùn)行結(jié)果 定制排序(在集合中寫(xiě)排序規(guī)則)TreeSet還有一種排序就是定制排序,定制排序時(shí)候,需要關(guān)聯(lián)一個(gè) Comparator對(duì)象,由Comparator提供排序邏輯。下面就是一個(gè)使用Lambda表達(dá)式代替Comparator對(duì)象來(lái)提供定制排序的例子。 下面是一個(gè)定制排序的列子 當(dāng)然將Comparator直接寫(xiě)入TreeSet初始化中也可以。如下。 TreeSet是依靠TreeMap來(lái)實(shí)現(xiàn)的。 Comparable和Comparator EnumSet特征EnumSet顧名思義就是專(zhuān)為枚舉類(lèi)型設(shè)計(jì)的集合,因此集合元素必須是枚舉類(lèi)型,否則會(huì)拋出異常。 EnumSet集合也是有序的,其順序就是Enum類(lèi)內(nèi)元素定義的順序。EnumSet存取的速度非常快,批量操作的速度也很快。EnumSet主要提供以下方法,allOf, complementOf, copyOf, noneOf, of, range等。注意到EnumSet并沒(méi)有提供任何構(gòu)造函數(shù),要?jiǎng)?chuàng)建一個(gè)EnumSet集合對(duì)象,只需要調(diào)用allOf等方法,下面是一個(gè)EnumSet的例子。 執(zhí)行結(jié)果
各種Set集合性能分析
Map 集合框架的第二類(lèi)接口樹(shù)。 實(shí)現(xiàn)類(lèi): HashMap、TreeMap、LinkedHashMap、Hashtable等
Map常用方法: 集合遍歷 1、增強(qiáng)for循環(huán) for(Obj o:c){syso(o)} 代碼實(shí)例 總結(jié)與面試 1.ArrayList: 元素單個(gè),效率高,多用于查詢(xún) 2.Vector: 元素單個(gè),線程安全,多用于查詢(xún) 3.LinkedList:元素單個(gè),多用于插入和刪除 4.HashMap: 元素成對(duì),元素可為空 5.HashTable: 元素成對(duì),線程安全,元素不可為空 HashMap和Hashtable的區(qū)別: HashMap和Hashtable都是java的集合類(lèi),都可以用來(lái)存放java對(duì)象,這是他們的相同點(diǎn) 以下是他們的區(qū)別: 1.歷史原因: Hashtable是基于陳舊的Dictionary類(lèi)的,HashMap是java 1.2引進(jìn)的Map接口的一個(gè)現(xiàn)實(shí)。 2.同步性: Hashtable是同步的,這個(gè)類(lèi)中的一些方法保證了Hashtable中的對(duì)象是線程安全的,而HashMap則是異步的,因此HashMap中的對(duì)象并不是線程安全的,因?yàn)橥降囊髸?huì)影響執(zhí)行的效率,所以如果你不需要線程安全的結(jié)合那么使用HashMap是一個(gè)很好的選擇,這樣可以避免由于同步帶來(lái)的不必要的性能開(kāi)銷(xiāo),從而提高效率,我們一般所編寫(xiě)的程序都是異步的,但如果是服務(wù)器端的代碼除外。 3.值: HashMap可以讓你將空值作為一個(gè)表的條目的key或value Hashtable是不能放入空值(null)的 ArrayList和Vector的區(qū)別: ArrayList與Vector都是java的集合類(lèi),都是用來(lái)存放java對(duì)象,這是他們的相同點(diǎn), 區(qū)別: 1.同步性: Vector是同步的,這個(gè)類(lèi)的一些方法保證了Vector中的對(duì)象的線程安全的,而ArrayList則是異步的,因此ArrayList中的對(duì)象并不 是線程安全的,因?yàn)橥揭髸?huì)影響執(zhí)行的效率,所以你不需要線程安全的集合那么使用ArrayList是一個(gè)很好的選擇,這樣可以避免由于同步帶來(lái)的不必 要的性能開(kāi)銷(xiāo)。 2.數(shù)據(jù)增長(zhǎng): 從內(nèi)部實(shí)現(xiàn)的機(jī)制來(lái)講,ArrayList和Vector都是使用數(shù)組(Array)來(lái)控制集合中的對(duì)象,當(dāng)你向兩種類(lèi)型中增加元素的時(shí)候,如果元素的數(shù)目超過(guò)了內(nèi)部數(shù)組目前的長(zhǎng)度他們都需要擴(kuò)展內(nèi)部數(shù)組的長(zhǎng)度,Vector缺省情況下自動(dòng)增長(zhǎng)原來(lái)一倍的數(shù)組長(zhǎng)度,ArrayList是原來(lái)的50%,所以最后你獲得的這個(gè)集合所占的空間總是比你實(shí)際需要的要大,所以如果你要在集合中保存大量的數(shù)據(jù),那么使用Vector有一些優(yōu)勢(shì),因?yàn)槟憧梢酝ㄟ^(guò)設(shè)置集合的初始大小來(lái)避免不必要的資源開(kāi)銷(xiāo)。 總結(jié): 1)如果要求線程安全,使用Vector,Hashtable 2)如果不要求線程安全,使用ArrayList,LinkedList,HashMap 3)如果要求鍵值對(duì),則使用HashMap,Hashtable 4)如果數(shù)據(jù)量很大,又要求線程安全考慮Vector arraylist和linkedlist聯(lián)系與區(qū)別 HashMap與TreeMap聯(lián)系與區(qū)別 同樣做測(cè)試: |
|
來(lái)自: Java幫幫 > 《待分類(lèi)》