目錄
目錄
介紹Java的List,ArrayList與LinkedList的區別
Array(數組)和ArrayList有何區別?什麼時候更適合用Array?
通過Array.asList獲得的List有何特點,使用時應該注意什麼?
ArrayList的刪除,需要注意List刪除後下標變化的坑(用Iterator的寫法)
什麼是fail-fast,什麼是fail-safe,有什麼區別嗎?
在迭代一個集合的時候,如何避免ConcurrentModificationException?
HashMap爲什麼不直接使用hashCode()處理後的哈希值直接作爲table的下標?
爲什麼HashMap中String、Integer這樣的包裝類適合作爲Key?我們能否使用任何類作爲Map的key?如果能需要注意哪些問題
ConcurrentHashMap和Hashtable的區別?
ConcurrentHashMap在JDK1.7和JDK1.8中有哪些不同?
集合在遍歷過程中是否可以刪除元素,爲什麼迭代器就可以安全刪除元素
一、集合框架概述
Java集合框架是一個很重要的內容,面試中也是屬於必考的部分,那麼首先,什麼是集合?什麼是集合框架呢?集合框架有什麼優點?
通常我們把具有相同性質的一類東西,匯聚成一個整體,就可以稱爲集合。集合框架是爲表示和操作集合而規定的一種統一的標準的體系結構。任何集合框架都包含三大塊內容:對外的接口、接口的實現和對集合運算的算法。
集合框架的優點:
(1)使用核心集合類降低開發成本,而非實現我們自己的集合類。
(2)隨着使用經過嚴格測試的集合框架類,代碼質量會得到提高。
(3)通過使用JDK附帶的集合類,可以降低代碼維護成本。
(4)複用性和可操作性。
集合框架常用實現類如下:
在集合框架中,主要可以分爲三個大類:list(列表)、set(集)、map(映射)
list是有序的,元素按照添加進list的順序排序,元素可以重複,訪問時可以根據元素的索引來訪問,使用Iterator時的遍歷順序和元素添加的順序一致;
set是無序的,因此使用迭代器(Iterator)的時候,不能保證元素的遍歷順序,但是可以保證所有元素被遍歷;此外,set中不允許出現重複的元素,訪問的時候只能通過元素本身來訪問(這也是元素不能重複的原因);
map保存的是<key,value>鍵值對,key不能重複,但是value可以重複,訪問時只能根據key來訪問value,使用Iterator時,對key進行遍歷,然後根據每個key訪問相應的value;
此外,還有queue接口和一些在併發中涉及到的集合。
二、面試重點
1、List接口
List接口是Collection接口的子接口,通常用於表示有序的數組(可以動態擴容),鏈表,隊列,棧等。其實現類主要有3個:ArrayList、LinkedList 和 Vector(線程安全)。
面試中主要涉及的問題有:
介紹Java的List,ArrayList與LinkedList的區別
ArrayList與LinkedList、Vector的區別
Array(數組)和ArrayList有何區別?什麼時候更適合用Array?
區別:
- 數組的長度固定不變,而ArrayList是動態數組,在容量不夠是會進行1.5倍的擴容
- 數組的元素可以是基本類型或者對象,但ArrayList只能是對象(直接放入的基本類型經過自動裝箱成爲包裝類對象)
- 數組中所有元素類型必須是一樣的,但是ArrayList可以存儲Object對象,因此如果把泛型類型限制爲Object則可以存儲不同類型的元素
- ArrayList比數組的方法更加豐富,還可以有自己的迭代器對象
使用場景:
- 當集合長度固定,並且只需要對數組進行簡單的隨機讀寫,使用數組,因爲數組佔用內存更低;如果集合長度可變,並且需要有插入、刪除等操作,選擇ArrayList,因爲ArrayList集成了相關操作,並且可以自動擴容。當然如果插入和刪除的操作十分的頻繁還是使用LinkedList。
- 當存儲的時基本數據類型,因爲ArrayList不支持基本數據類型,需要進行自動裝箱和拆箱,效率相對下降,因此儘量選擇數組。
List是線程安全的嗎?如果要線程安全要怎麼做?
實現list實現類的線程安全:
- List接口下的實現類,只有Vector及其子類Stack時線程安全的,其他都是非線程安全的,因此可以使用Vector來代替ArrayList
- 可以使用Collections.synchronizedList(List<T> list)方法包裝list的實現類即可,同樣的可以用Collections.synchronizedSet(Set<T> set)、Collections.synchronizedMap(Map<K,V> m)、Collections.synchronizedCollection(Collection<Object>)方法包裝集合框架的其他非線程安全實現類來實現線程安全的類。
兩種方法的區別:
-
如果使用add方法,那麼他們的擴容機制不一樣(類似arraylist和Vector的區別)。
-
SynchronizedList可以通過Collections.synchronizedList(List<T> list,Object mutex)指定鎖定的對象。通過Collections的源代碼也可以看出Collections.synchronizedList返回的是內部類SynchronizedList,其中同步的實現是通過synchronized(mutex)同步代碼塊實現的,可以認爲Collections.synchronizedList鎖粒度是同步代碼塊。而Vector的鎖粒度是同步方法。
-
SynchronizedList有很好的擴展性和兼容功能。他可以將所有的list子類轉成線程安全的類。
-
使用SynchronizedList的時候,進行遍歷時需要手動進行同步處理,這是因爲Collections.synchronizedList的迭代器iterator並沒有做同步處理,而是直接使用了list的iterator。
怎麼給List排序?
通過Array.asList獲得的List有何特點,使用時應該注意什麼?
asList()方法返回的list的特點:
Array.asList()方法的作用是將數組轉換爲list集合,但是通過其源代碼可以發現,Array.asList()返回的是Arrays的一個內部類,該內部類只是實現了AbstractList接口,但是其後臺仍然是數組的數據結構,也就是說,得到的只是原來數組的視圖List,而沒有實現list集合的add和remove等修改方法,因此如果對他進行增刪操作會報UnsupportOperationException異常。
具體案例參考:https://www.cnblogs.com/hoobey/p/6661294.html
如何實現得到真的list:
用ArrayList的構造器可以將其轉變成爲一個真正的ArrayList
Integer[] a={1,2,3,4,5};
ArrayList al= new ArrayList(Arrays.asList(a));
需要注意的是,Array.asList()方法對基本數據類型的數組是無效的:如下代碼所示,對於基本數據類型,ArrayList中的元素只有一個,就是數組本身。
int[] a={1,2,3,4,5};
ArrayList al= new ArrayList(Arrays.asList(a));
System.out.println(al.size());
//返回:1
List和Array之間如何互相轉換?
- 數組(Array)轉list:Array.asList()方法,同上
- list轉數組(Array):list.toArray()方法
ArrayList的刪除,需要注意List刪除後下標變化的坑(用Iterator的寫法)
什麼是fail-fast,什麼是fail-safe,有什麼區別嗎?
fail-fast 與 fail-safe 機制的原理與區別
List的迭代方式有哪些?
Collection集合實現類(list、set和queue)的集中迭代方式
在迭代一個集合的時候,如何避免ConcurrentModificationException?
什麼時候會出現ConcurrentModificationException異常:
首先,ConcurrentModificationException異常是當條件modCount != expectedModCount成立時產生的,而這個比較是在迭代器(Iterator)的checkForComodification()方法中出現的,而Iterator的next()和remove()都調用了該方法,而expectedModCount是在獲取迭代器的時候初始化的,也就是說,如果在調用Iterator的next()和remove()時modCount被修改,那麼checkForComodification()方法會拋出ConcurrentModificationException異常。那麼,什麼情況會導致modCount被修改呢?add()、remove()操作都會。(foreach循環底層也是使用迭代器因此也hi出現該異常)
因此,在使用迭代器(Iterator)的時候使用list.add()、list.clear()和list.remove()方法都會導致ConcurrentModificationException異常。
詳細代碼參考:https://www.jianshu.com/p/00be866fcb18
如何避免ConcurrentModificationException異常:
- 如果是remove()操作,使用迭代器(Iterator)自己的reemove()方法;listIterator有add()和remove()方法;
- 使用Collections.synchronizedCollection() 去同步集合, 但是這樣可能會影響效率,;
- 使用concurrent包裏的CopyOnWriteArrayList, 因爲他的迭代器中沒有checkForComodification;
2、set接口
set也是Collection接口的子接口,常見實現類有HashSet和TreeSet 。set集合中不允許有重複的元素是因爲set集合用來比較兩個元素是否相等時使用的是equals()方法,而不是“==”運算符,因此只要兩個元素內容相同,就不能在一個set中共存,如果已經在set集合中存在了a元素,再使用add()方法添加a元素會返回false。
面試中主要涉及的問題有:
HashSet是如何保證數據不可重複的?
HashSet 底層數據結構
LinkedHashSet 底層數據結構及其特點
TreeSet的底層數據結構及其特點
3、map接口
map存儲的是<key,value>鍵值對,而每個值又可以是一個map,以此類推,可以實現多層的映射。map接口常見的實現類有HashMap,TreeMap,HashTable等。map的key和set一樣,不允許重複,但是value可以重複。從概念上來看,可以把List看做一種特殊的map,把List的索引當做key,把list的元素當做value,這樣就可以形成鍵值對,但是事實上List和map並沒有太多的關係。
面試中主要涉及的問題有:
HashMap的底層數據結構
HashMap與HashTable的區別?
HashMap是否線程安全,體現在什麼地方?
HashMap的擴容操作是怎麼實現的?
爲什麼數組長度要保持爲2的冪次方?
HashMap是怎麼解決哈希衝突的?
HashMap爲什麼不直接使用hashCode()處理後的哈希值直接作爲table的下標?
HashMap在JDK1.7和JDK1.8中有哪些不同?
爲什麼HashMap中String、Integer這樣的包裝類適合作爲Key?我們能否使用任何類作爲Map的key?如果能需要注意哪些問題
HashMap和HashTable有何不同?
ConcurrentHashMap和Hashtable的區別?
ConcurrentHashMap在JDK1.7和JDK1.8中有哪些不同?
4、Queue接口
BlockingQueue的特點?
隊列和棧的區別?