Java 容器詳解
一.Java 容器都有哪些?
Java 容器分為 Collection 和 Map 兩大類,其下又有很多子類,如下所示:
Collection List ArrayList LinkedList Vector Stack Set HashSet LinkedHashSet TreeSet Map HashMap
LinkedHashMap TreeMap ConcurrentHashMap Hashtable
二.Collection 和 Collections 有什么區(qū)別?
Collection 是一個集合接口,它提供了對集合對象進行基本操作的通用接口方法,所有集合都是它的子類,比如
List、Set 等。 Collections 是一個包裝類,包含了很多靜態(tài)方法,不能被實例化,就像一個工具類,比如提供的排序方法:Collections. sort(list)。
三.List、Set、Map 之間的區(qū)別是什么?
List、Set、Map 的區(qū)別主要體現(xiàn)在兩個方面:元素是否有序、是否允許元素重復(fù)。 三者之間的區(qū)別,如下表:
1. 三者之間的區(qū)別如下:
1) 元素重復(fù)性:
① List允許有重復(fù)的元素。任何數(shù)量的重復(fù)元素都可以在不影響現(xiàn)有重復(fù)元素的值及其索引的情況下插入到List集合中;
② Set集合不允許元素重復(fù)。Set以及所有實現(xiàn)了Set接口的類都不允許重復(fù)值的插入,若多次插入同一個元素時,在該集合中只顯示一個;
③ Map以鍵值對的形式對元素進行存儲。Map不允許有重復(fù)鍵,但允許有不同鍵對應(yīng)的重復(fù)的值;
2) 元素的有序性:
① List及其所有實現(xiàn)類保持了每個元素的插入順序;
② Set中的元素都是無序的;但是某些Set的實現(xiàn)類以某種殊形式對其中的元素進行排序,如:LinkedHashSet按照元素的插入順序進行排序;
③ Map跟Set一樣對元素進行無序存儲,但其某些實現(xiàn)類對元素進行了排序。如:TreeMap根據(jù)鍵對其中的元素進行升序排序;
3) 元素是否為空值:
① List允許任意數(shù)量的空值;
② Set最多允許一個空值的出現(xiàn);[ 當向Set集合中添加多個null值時,在該Set集合中只會顯示一個null元素]
③ Map只允許出現(xiàn)一個空鍵,但允許出現(xiàn)任意數(shù)量的空值;
總結(jié): List中的元素,有序、可重復(fù)、可為空;
Set中的元素,無序、不重復(fù)、只有一個空元素;
Map中的元素,無序、鍵不重,值可重、可一個空鍵、多可空值;
2. 實現(xiàn)類:
① List:ArrayList、LinkedList;
② Set:HashSet、LinkedHashSet、TreeSet、SortedSet等等;③ Map:HashMap、TreeMap、WeakHashMap、LinkedHashMap、IdentityHashMap等等;
3. List集合的子類ArrayList、Vector、LinkedList之間的區(qū)別:
ArrayList和Vector都是以數(shù)組的方式存儲數(shù)據(jù)的,此數(shù)組長度大于實際存儲元素個數(shù),以方便插入元素;它們都允許直接按索引獲取元素;由于在插入數(shù)據(jù)時,涉及到數(shù)組元素的移動等內(nèi)存操作,所以在插入數(shù)據(jù)時執(zhí)行速度較慢;Vector是線程安全的(synchronized),所以性能上要比ArrayList差;而LinkedList是以雙向鏈表的形式存儲數(shù)據(jù)的,在按索引獲取數(shù)據(jù)時只需要向前或者向后進行遍歷即可;在插入數(shù)據(jù)時,只需要記錄本項的前后項即可,所以插入速度較快。
4. HashMap和HashTable的區(qū)別:
HashMap時HashTable的輕量級實現(xiàn)(非線程安全的實現(xiàn)),它們都實現(xiàn)了Map接口,主要區(qū)別在于HashMap允許空(null)鍵值(key),由于非線程安全,效率上高于HashTable。HashMap允許將null作為一個entry的key或者value,而HashTable不允許。HashMap去掉 了HashTable的contains方法,改成containsValue和containsKey方法。二者最大的不同是,HashTable的方法是synchronized(線程安全的),而HashMap不是,在多個線程訪問HashTable時,不需要自己為它的方法實現(xiàn)同步,而HashMap就必須為之提供外同步。
21. HashMap 和 Hashtable 有什么區(qū)別?
HashMap是繼承自AbstractMap類,而HashTable是繼承自Dictionary類。不過它們都實現(xiàn)了同時實現(xiàn)了map、Cloneable(可復(fù)制)、Serializable(可序列化)這三個接口。 Hashtable比HashMap多提供了elments() 和contains() 兩個方法。 HashMap的key-value支持key-value,null-null,key-null,null-value四種。而Hashtable只支持key-value一種(即key和value都不為null這種形式)。既然HashMap支持帶有null的形式,那么在HashMap中不能由get()方法來判斷HashMap中是否存在某個鍵, 而應(yīng)該用containsKey()方法來判斷,因為使用get的時候,當返回null時,你無法判斷到底是不存在這個key,還是這個key就是null,還是key存在但value是null。 線程安全性不同:HashMap的方法都沒有使用synchronized關(guān)鍵字修飾,都是非線程安全的,而Hashtable的方法幾乎都是被synchronized關(guān)鍵字修飾的。但是,當我們需要HashMap是線程安全的時,怎么辦呢?我們可以通過Collections.synchronizedMap(hashMap)來進行處理,亦或者我們使用線程安全的ConcurrentHashMap。ConcurrentHashMap雖然也是線程安全的,但是它的效率比Hashtable要高好多倍。因為
ConcurrentHashMap使用了分段鎖,并不對整個數(shù)據(jù)進行鎖定。 初始容量大小和每次擴充容量大小的不同:Hashtable默認的初始大小為11,之后每次擴充,容量變?yōu)樵瓉淼?n+1。HashMap默認的初始化大小為16。之后每次擴充,容量變?yōu)樵瓉淼?倍。 計算hash值的方法不同:為了得到元素的位置,首先需要根據(jù)元素的 KEY計算出一個hash值,然后再用這個hash值來計算得到最終的位置。Hashtable直接使用對象的hashCode。hashCode是JDK根據(jù)對象的地址或者字符串或者數(shù)字算出來的int類型的數(shù)值。然后再使用除留余數(shù)發(fā)來獲得最終的位置。
22.如何決定使用 HashMap 還是 TreeMap?
對于在 Map 中插入、刪除、定位一個元素這類操作,HashMap 是最好的選擇,因為相對而言 HashMap 的插入會更快,但如果你要對一個 key 集合進行有序的遍歷,那 TreeMap 是更好的選擇。
23.說一下 HashMap 的實現(xiàn)原理?
HashMap 基于 Hash 算法實現(xiàn)的,我們通過 put(key,value)存儲,get(key)來獲取。當傳入 key 時,HashMap 會根據(jù) key. hashCode() 計算出 hash 值,根據(jù) hash 值將 value 保存在 bucket 里。當計算出的 hash 值相同時,我們稱之為 hash 沖突,HashMap 的做法是用鏈表和紅黑樹存儲相同 hash 值的 value。當 hash 沖突的個數(shù)比較少時,使用鏈表否則使用紅黑樹。24.說一下 HashSet 的實現(xiàn)原理?HashSet 是基于 HashMap 實現(xiàn)的,HashSet 底層使用 HashMap 來保存所有元素,因此 HashSet 的實現(xiàn)比較簡單,相關(guān) HashSet 的操作,基本上都是直接調(diào)用底層 HashMap 的相關(guān)方法來完成,HashSet 不允許重復(fù)的值。25.ArrayList 和 LinkedList 的區(qū)別是什么?數(shù)據(jù)結(jié)構(gòu)實現(xiàn):ArrayList 是動態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu)實現(xiàn),而 LinkedList 是雙向鏈表的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)。 隨機訪問效率:ArrayList 比 LinkedList 在隨機訪問的時候效率要高,因為 LinkedList 是線性的數(shù)據(jù)存儲方式,所以需要移動指針從前往后依次查找。 增加和刪除效率:在非首尾的增加和刪除操作,LinkedList 要比 ArrayList 效率要高,因為 ArrayList 增刪操作要影響數(shù)組內(nèi)的其他數(shù)據(jù)的下標。 綜合來說,在需要頻繁讀取集合中的元素時,更推薦使用ArrayList,而在插入和刪除操作較多時,更推薦使用 LinkedList。
26.如何實現(xiàn)數(shù)組和 List 之間的轉(zhuǎn)換?
數(shù)組轉(zhuǎn) List:使用 Arrays. asList(array) 進行轉(zhuǎn)換。 List 轉(zhuǎn)數(shù)組:使用 List 自帶的 toArray() 方法。 代碼示例:
27.ArrayList 和 Vector 的區(qū)別是什么?
線程安全:Vector 使用了 Synchronized 來實現(xiàn)線程同步,是線程安全的,而 ArrayList 是非線程安全的。 性能:ArrayList 在性能方面要優(yōu)于 Vector。 擴容:ArrayList 和 Vector 都會根據(jù)實際的需要動態(tài)的調(diào)整容量,只不過在Vector 擴容每次會增加 1 倍,而 ArrayList 只會增加 50%。
28.Array 和 ArrayList 有何區(qū)別?
Array 可以存儲基本數(shù)據(jù)類型和對象,ArrayList 只能存儲對象。 Array 是指定固定大小的,而 ArrayList 大小是自動擴展的。 Array 內(nèi)置方法沒有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。
29.在 Queue 中 poll()和 remove()有什么區(qū)別?
相同點:都是返回第一個元素,并在隊列中刪除返回的對象。 不同點:如果沒有元素 remove()會直接拋出NoSuchElementException 異常,而 poll()會返回 null。 代碼示例:
Queue<String> queue = new LinkedList<String>();
queue. offer("string"); // add
System. out. println(queue. poll());
System. out. println(queue. remove());
System. out. println(queue. size());
30.哪些集合類是線程安全的?
Vector、Hashtable、Stack 都是線程安全的,而像 HashMap 則是非線程安全的,不過在 JDK 1.5 之后隨著 Java.util. concurrent 并發(fā)包的出現(xiàn),它們也有了自己對應(yīng)的線程安全類,比如 HashMap 對應(yīng)的線程安全類就是ConcurrentHashMap。
31.迭代器 Iterator 是什么?
Iterator 接口提供遍歷任何 Collection 的接口。我們可以從一個 Collection 中使用迭代器方法來獲取迭代器實例。迭代器取代了 Java 集合框架中的 Enumeration,迭代器允許調(diào)用者在迭代過程中移除元素。
32.Iterator 怎么使用?有什么特點?
Iterator 使用代碼如下:
List<String> list = new ArrayList<>(); Iterator<String> it = list. iterator(); while(it. hasNext()){ String obj = it. next(); System. out. println(obj); }
Iterator 的特點是更加安全,因為它可以確保,在當前遍歷的集合元素被更改的時候,就會拋出ConcurrentModifificationException 異常。
33.Iterator 和 ListIterator 有什么區(qū)別?
Iterator 可以遍歷 Set 和 List 集合,而 ListIterator 只能遍歷 List。 Iterator 只能單向遍歷,而 ListIterator 可以雙向遍歷(向前/后遍歷)。 ListIterator 從 Iterator 接口繼承,然后添加了一些額外的功能,比如添加一個元素、替換一個元素、獲取前面或后面元素的索引位置。
34.怎么確保一個集合不能被修改?
可以使用 Collections. unmodififiableCollection(Collection c) 方法來創(chuàng)建一個只讀集合,這樣改變集合的任何操作都會拋出 Java. lang. UnsupportedOperationException 異常。 示例代碼如下:
List<String> list = new ArrayList<>(); list. add("x"); Collection<String> clist = Collections. unmodifiableCollection(list); clist. add("y"); // 運行時此行報錯 System. out. println(list. size());
浙公網(wǎng)安備 33010602011771號