- 問(wèn) Java中有哪些容器(集合類)
答: Java中的集合類主要是由Collection和Map這兩個(gè)接口派生出來(lái)的,其中Collection接口又派生出三個(gè)子接口,分別是Set,List,Queue.所有的Java集合類,都是Set,List,Queue.Map這四個(gè)接口的實(shí)現(xiàn)類,這四個(gè)接口將集合分成了四個(gè)大類,其中
- Set代表無(wú)序的,元素不可重復(fù)的集合.
- List代表有序的,元素可以重復(fù)的集合.
- Queue代表先進(jìn)先出(FIFO)的隊(duì)列
- Map代表具有映射關(guān)系(Key-Value)的集合
拓展閱讀
Collection體系的繼承樹

Map體系的繼承樹

注:紫色框體代表接口,其中加粗的代表四類集合的接口. 藍(lán)色框代表實(shí)現(xiàn)類,其中有陰影的是常用實(shí)現(xiàn)類
- 問(wèn):Java中的容器,線程安全和線程不安全的分別有哪些?
答: java.util包下的集合類大部分都是線程不安全的,例如常用的HashSet.TreeSet.ArrayList.LinkedList.ArrayDeque.HashMap.TreeMap,這些都是線程不安全的集合類,優(yōu)點(diǎn)是性能好. 如果需要使用線程安全的集合類,則可以使用Collections工具類提供的synchronizedXxx(),將這些集合類包裝成線程安全的集合類
java.util包下也有線程安全的集合類,例如Vector,Hashtable.這些集合類都是比較古老的API. 雖然實(shí)現(xiàn)了線程安全,但是性能很差.
從Java5開始,Java在java.util.concurrent包下提供了大量支持高效并發(fā)訪問(wèn)的集合類,它們既能包裝良好的訪問(wèn)性能,有能包裝線程安全.這些集合類可以分為兩部分,它們的特征如下:
- 以Concurrent開頭的集合類:
以Concurrent開頭的集合類代表了支持并發(fā)訪問(wèn)的集合,它們可以支持多個(gè)線程并發(fā)寫入訪問(wèn),這些寫入的線程的所有操作都是線程安全的,但讀取操作不必鎖定.以Concurrent開頭的集合類采用了更復(fù)雜的算法來(lái)保證永遠(yuǎn)不會(huì)鎖住整個(gè)集合,因此在并發(fā)寫入時(shí)有較好的性能. - 以CopyOnWrite開頭的集合類:
以CopyOnWrite開頭的集合類采用復(fù)制底層數(shù)組的方式來(lái)實(shí)現(xiàn)寫操作.當(dāng)線程對(duì)此類集合執(zhí)行讀操作時(shí),線程將會(huì)直接讀取集合本身,無(wú)須加鎖與阻塞.當(dāng)線程對(duì)此類集合執(zhí)行寫入操作時(shí),集合會(huì)在底層復(fù)制一份新的數(shù)組,接下來(lái)對(duì)新的數(shù)組執(zhí)行寫入操作. 由于對(duì)集合的寫入操作都是對(duì)數(shù)組的副本執(zhí)行操作,因此它是線程安全的.
拓展閱讀
java.util.concurrent包下的線程安全的類的體系結(jié)構(gòu)

- 問(wèn) Map接口有哪些實(shí)現(xiàn)類?
答:常用的有HashMap.LinkedHashMap.TreeMap.ConcurrentHashMap.
對(duì)于無(wú)需排序的場(chǎng)景,優(yōu)先考慮HashMap. 因?yàn)樗切阅茏詈玫腗ap實(shí)現(xiàn). 如果要保證線程安全,則使用ConcurrentHashMap. 它的性能好于Hashtable,因?yàn)樵趐ut時(shí)會(huì)采用分段鎖/CAS的加鎖機(jī)制,而不是像HashTable,put和get都做同步處理.
如果需要排序,按插入順序排序則可以使用LinkHashMap. 如果要Key按照自然順序排序甚至自定義順序排序,則可以選擇TreeMap. 如果要保證線程安全,則可以使用Collections工具類包裝成線程安全的Map - 問(wèn): 描述下Map put的過(guò)程
答: HashMap最經(jīng)典,以此為例
- 首次擴(kuò)容
先判斷數(shù)組是否為空,若數(shù)組為空則進(jìn)行第一次擴(kuò)容(resize) - 計(jì)算索引
通過(guò)hash算法,計(jì)算鍵值對(duì)在數(shù)組中的索引 - 插入數(shù)據(jù)
如果當(dāng)前位置為空,則直接插入數(shù)據(jù)
如果當(dāng)前位置元素非空,且Key存在,則直接覆蓋其value
如果當(dāng)前位置元素非空,且key不存在,則將數(shù)據(jù)鏈到鏈表末端
若鏈表長(zhǎng)度達(dá)到8,則將鏈表轉(zhuǎn)成紅黑樹,并將數(shù)據(jù)插入樹中 - 再次擴(kuò)容
如果數(shù)組元素個(gè)數(shù)(size)吃過(guò)threshold,則再次進(jìn)行擴(kuò)容操作.
拓展閱讀
HashMap添加數(shù)據(jù)的詳細(xì)過(guò)程

- 問(wèn): 如何得到一個(gè)線程安全的Map?
答: 1. 使用Collections工具類,將線程不安全的Map包裝成線程安全的Map;
- 使用java.util.concurrent包下的Map,如ConcurrentHashMap;
- 不建議使用HashTable,雖然線程安全,但性能較差.
- 問(wèn) HashMap有啥特點(diǎn)
- HashMap是線程不安全的實(shí)現(xiàn),高效率
- HashMap可以使用null作為key或value
HashMap為什么線程不安全?
答: HashMap在并發(fā)執(zhí)行put操作時(shí),可能會(huì)導(dǎo)致形成循環(huán)鏈表,從而引起死循環(huán).HashMap如何實(shí)現(xiàn)線程安全?
- 直接使用Hashtable類
- 直接使用ConcurrentHashMap
- 使用Collections將HashMap包裝成線程安全的Map
-
問(wèn) 介紹下ConcurrentHashMap是如何實(shí)現(xiàn)的?
答: jdk1.7和jdk1.8中不同
在1.7中ConcurrentHashMap由Segment數(shù)據(jù)結(jié)構(gòu)和HashEntry數(shù)組結(jié)構(gòu)構(gòu)成. 采取分段鎖來(lái)保證安全性. Segment扮演鎖的角色,HashEntry用于存儲(chǔ)鍵值對(duì)數(shù)據(jù).
在jdk1.7中ConcurrentHashMap的數(shù)據(jù)結(jié)構(gòu)
在1.8中的實(shí)現(xiàn)已經(jīng)摒棄了Segment的概念,直接使用了Node數(shù)組+鏈表+紅黑樹的數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn).并發(fā)控制使用Synchronized和CAS來(lái)操作.
