更新時間:2019-09-10 14:53:37 來源:動力節點 瀏覽2532次
主要內容:
Arraylist與LinkedList異同
ArrayList與Vector區別
HashMap的底層實現
HashMap和Hashtable的區別
HashMap的長度為什么是2的冪次方
HashSet和HashMap區別
ConcurrentHashMap和Hashtable的區別
ConcurrentHashMap線程安全的具體實現方式/底層具體實現
集合框架底層數據結構總結
Arraylist與LinkedList異同
1.是否保證線程安全:ArrayList和LinkedList都是不同步的,也就是不保證線程安全;
2.底層數據結構:Arraylist底層使用的是Object數組;LinkedList底層使用的是雙向循環鏈表數據結構;
3.插入和刪除是否受元素位置的影響:①ArrayList采用數組存儲,所以插入和刪除元素的時間復雜度受元素位置的影響。比如:執行add(Ee)方法的時候,ArrayList會默認在將指定的元素追加到此列表的末尾,這種情況時間復雜度就是O(1)。但是如果要在指定位置i插入和刪除元素的話(add(intindex,Eelement))時間復雜度就為O(n-i)。因為在進行上述操作的時候集合中第i和第i個元素之后的(n-i)個元素都要執行向后位/向前移一位的操作。②LinkedList采用鏈表存儲,所以插入,刪除元素時間復雜度不受元素位置的影響,都是近似O(1)而數組為近似O(n)。
4.是否支持快速隨機訪問:LinkedList不支持高效的隨機元素訪問,而ArrayList實現了RandmoAccess接口,所以有隨機訪問功能。快速隨機訪問就是通過元素的序號快速獲取元素對象(對應于get(intindex)方法)。
5.內存空間占用:ArrayList的空間浪費主要體現在在list列表的結尾會預留一定的容量空間,而LinkedList的空間花費則體現在它的每一個元素都需要消耗比ArrayList更多的空間(因為要存放直接后繼和直接前驅以及數據)。
補充:數據結構基礎之雙向鏈表
雙向鏈表也叫雙鏈表,是鏈表的一種,它的每個數據結點中都有兩個指針,分別指向直接后繼和直接前驅。所以,從雙向鏈表中的任意一個結點開始,都可以很方便地訪問它的前驅結點和后繼結點。一般我們都構造雙向循環鏈表,如下圖所示,同時下圖也是LinkedList底層使用的是雙向循環鏈表數據結構。
ArrayList與Vector區別
Vector類的所有方法都是同步的。可以由兩個線程安全地訪問一個Vector對象、但是一個線程訪問Vector的話代碼要在同步操作上耗費大量的時間。
Arraylist不是同步的,所以在不需要保證線程安全時時建議使用Arraylist。
HashMap的底層實現
JDK1.8之前
JDK1.8之前HashMap由數組+鏈表組成的(“鏈表散列”即數組和鏈表的結合體),數組是HashMap的主體,鏈表則是主要為了解決哈希沖突而存在的(HashMap采用“拉鏈法也就是鏈地址法”解決沖突),如果定位到的數組位置不含鏈表(當前entry的next指向null),那么對于查找,添加等操作很快,僅需一次尋址即可;如果定位到的數組包含鏈表,對于添加操作,其時間復雜度依然為O(1),因為最新的Entry會插入鏈表頭部,即需要簡單改變引用鏈即可,而對于查找操作來講,此時就需要遍歷鏈表,然后通過key對象的equals方法逐一比對查找.
所謂“拉鏈法”就是將鏈表和數組相結合。也就是說創建一個鏈表數組,數組中每一格就是一個鏈表。若遇到哈希沖突,則將沖突的值加到鏈表中即可。
JDK1.8之后
相比于之前的版本,JDK1.8之后在解決哈希沖突時有了較大的變化,當鏈表長度大于閾值(默認為8)時,將鏈表轉化為紅黑樹,以減少搜索時間。
TreeMap、TreeSet以及JDK1.8之后的HashMap底層都用到了紅黑樹。紅黑樹就是為了解決二叉查找樹的缺陷,因為二叉查找樹在某些情況下會退化成一個線性結構。
HashMap和Hashtable的區別
線程是否安全:HashMap是非線程安全的,HashTable是線程安全的;HashTable內部的方法基本都經過synchronized修飾。(如果你要保證線程安全的話就使用ConcurrentHashMap吧!);
效率:因為線程安全的問題,HashMap要比HashTable效率高一點。另外,HashTable基本被淘汰,不要在代碼中使用它;
對Nullkey和Nullvalue的支持:HashMap中,null可以作為鍵,這樣的鍵只有一個,可以有一個或多個鍵所對應的值為null。。但是在HashTable中put進的鍵值只要有一個null,直接拋出NullPointerException。
初始容量大小和每次擴充容量大小的不同:①創建時如果不指定容量初始值,Hashtable默認的初始大小為11,之后每次擴充,容量變為原來的2n+1。HashMap默認的初始化大小為16。之后每次擴充,容量變為原來的2倍。②創建時如果給定了容量初始值,那么Hashtable會直接使用你給定的大小,而HashMap會將其擴充為2的冪次方大小。也就是說HashMap總是使用2的冪次方作為哈希表的大小,后面會介紹到為什么是2的冪次方。
底層數據結構:JDK1.8以后的HashMap在解決哈希沖突時有了較大的變化,當鏈表長度大于閾值(默認為8)時,將鏈表轉化為紅黑樹,以減少搜索時間。Hashtable沒有這樣的機制。
HashMap的長度為什么是2的冪次方
為了能讓HashMap存取高效,盡量較少碰撞,也就是要盡量把數據分配均勻,每個鏈表/紅黑樹長度大致相同。這個實現就是把數據存到哪個鏈表/紅黑樹中的算法。
這個算法應該如何設計呢?
我們首先可能會想到采用%取余的操作來實現。但是,重點來了:“取余(%)操作中如果除數是2的冪次則等價于與其除數減一的與(&)操作(也就是說hash%length==hash&(length-1)的前提是length是2的n次方;)。”并且采用二進制位操作&,相對于%能夠提高運算效率,這就解釋了HashMap的長度為什么是2的冪次方。
HashSet和HashMap區別
ConcurrentHashMap和Hashtable的區別
ConcurrentHashMap和Hashtable的區別主要體現在實現線程安全的方式上不同。
底層數據結構:JDK1.7的ConcurrentHashMap底層采用分段的數組+鏈表實現,JDK1.8采用的數據結構跟HashMap1.8的結構類似,數組+鏈表/紅黑二叉樹。Hashtable和JDK1.8之前的HashMap的底層數據結構類似都是采用數組+鏈表的形式,數組是HashMap的主體,鏈表則是主要為了解決哈希沖突而存在的;
實現線程安全的方式(重要):①在JDK1.7的時候,ConcurrentHashMap(分段鎖)對整個桶數組進行了分割分段(Segment),每一把鎖只鎖容器其中一部分數據,多線程訪問容器里不同數據段的數據,就不會存在鎖競爭,提高并發訪問率。(默認分配16個Segment,比Hashtable效率提高16倍。)到了JDK1.8的時候已經摒棄了Segment的概念,而是直接用Node數組+鏈表/紅黑樹的數據結構來實現,并發控制使用synchronized和CAS來操作。(JDK1.6以后對synchronized鎖做了很多優化)整個看起來就像是優化過且線程安全的HashMap,雖然在JDK1.8中還能看到Segment的數據結構,但是已經簡化了屬性,只是為了兼容舊版本;②Hashtable(同一把鎖):使用synchronized來保證線程安全,效率非常低下。當一個線程訪問同步方法時,其他線程也訪問同步方法,可能會進入阻塞或輪詢狀態,如使用put添加元素,另一個線程不能使用put添加元素,也不能使用get,競爭越激烈效率越低。
兩者的對比圖:
JDK1.7的ConcurrentHashMap:
JDK1.8的ConcurrentHashMap(TreeBin:紅黑二叉樹節點;Node:鏈表節點):
ConcurrentHashMap線程安全的具體實現方式/底層具體實現
JDK1.7(上面有示意圖)
首先將數據分為一段一段的存儲,然后給每一段數據配一把鎖,當一個線程占用鎖訪問其中一個段數據時,其他段的數據也能被其他線程訪問。
ConcurrentHashMap是由Segment數組結構和HahEntry數組結構組成。
Segment實現了ReentrantLock,所以Segment是一種可重入鎖,扮演鎖的角色。HashEntry用于存儲鍵值對數據。
staticclassSegment<K,V>extendsReentrantLockimplementsSerializable{}
一個ConcurrentHashMap里包含一個Segment數組。Segment的結構和HashMap類似,是一種數組和鏈表結構,一個Segment包含一個HashEntry數組,每個HashEntry是一個鏈表結構的元素,每個Segment守護著一個HashEntry數組里的元素,當對HashEntry數組的數據進行修改時,必須首先獲得對應的Segment的鎖。
JDK1.8(上面有示意圖)
ConcurrentHashMap取消了Segment分段鎖,采用CAS和synchronized來保證并發安全。數據結構跟HashMap1.8的結構類似,數組+鏈表/紅黑二叉樹。
synchronized只鎖定當前鏈表或紅黑二叉樹的首節點,這樣只要hash不沖突,就不會產生并發,效率又提升N倍。
集合框架底層數據結構總結
Collection
1.List
Arraylist:Object數組
Vector:Object數組
LinkedList:雙向循環鏈表
2.Set
HashSet(無序,唯一):基于HashMap實現的,底層采用HashMap來保存元素。HashMap底層數據結構見下。
LinkedHashSet:LinkedHashSet繼承與HashSet,并且其內部是通過LinkedHashMap來實現的。有點類似于我們之前說的LinkedHashMap其內部是基于Hashmap實現一樣,不過還是有一點點區別的。
TreeSet(有序,唯一):紅黑樹(自平衡的排序二叉樹。)
Map
HashMap:JDK1.8之前HashMap由數組+鏈表組成的,數組是HashMap的主體,鏈表則是主要為了解決哈希沖突而存在的(“拉鏈法”解決沖突).JDK1.8以后在解決哈希沖突時有了較大的變化,當鏈表長度大于閾值(默認為8)時,將鏈表轉化為紅黑樹,以減少搜索時間
LinkedHashMap:LinkedHashMap繼承自HashMap,所以它的底層仍然是基于拉鏈式散列結構即由數組和鏈表或紅黑樹組成。另外,LinkedHashMap在上面結構的基礎上,增加了一條雙向鏈表,使得上面的結構可以保持鍵值對的插入順序。同時通過對鏈表進行相應的操作,實現了訪問順序相關邏輯。詳細可以查看:《LinkedHashMap源碼詳細分析(JDK1.8)》:https://www.imooc.com/article/22931
HashTable:數組+鏈表組成的,數組是HashMap的主體,鏈表則是主要為了解決哈希沖突而存在的
TreeMap:紅黑樹(自平衡的排序二叉樹)
以上就是動力節點Java培訓機構介紹的“這幾道Java集合框架面試題在面試中幾乎必問”的內容,希望通過此文,能夠幫助到那些正在找工作的Java程序員,更多Java面試題請在線咨詢,有專業老師為你提供名企Java面試題。
相關推薦
Java面試題請點擊:http://www.dabaquan.cn/tutorial_baseinterviewquestions/
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習