[Java程序員面試筆記] 面試筆試部分 --Java容器

如果一個類是專門用來存儲其他類的對象,則這個類成爲容器類,也叫做集合類。Java中有一個容器類的類庫,其用途是保存對象或者保存對象的引用。
Java中的容器類主要從兩個不同的接口(Collection和Map)派生出來,這兩個不同的接口定義了不同的對象存儲方式。
1)Collection :定義獨立元素的序列
2)Map:定義成對的鍵值對(Key- Value),一個Map不能包含重複的鍵(Key)
Java中容器類的基本架構如下圖所示:

1 Collection和Iterator

知識點梳理

  • 1 Collection接口
    Collection接口是一類用於保存單一元素的數據結構的接口。Collection接口是容器類的根接口之一,它有3個子接口:List、Set和Queue。
    1)List代表元素有序的、可重複的集合
    2)Set代表元素無序的、不可重複的集合
    3)Queue代表隊列集合,它是一種特殊的線性表,只允許在表的前端刪除元素,在表的後端插入元素。

List在元素輸出時保證了與元素添加時的順序一致,並允許元素重複,這一點類似於一個線性表(數組或鏈表)。
Set表示一個無序的不可重複的集合,Set打破了元素添加時的順序,不保證按照元素添加時的順序輸出,也不進行排序,同時在添加元素的過程中過濾掉了重複元素。
Queue提供了隊列的實現方法,滿足FIFO(即先進先出)原則。

  • Iterator 接口
    Iterator 接口直譯爲迭代器,它可以在不知道容器中元素類型的情況下遍歷容器中的元素

面試題1 常識性問題

對於Collection和Collections類的區分:
(1)Collection是一類用於實現保存單一元素的數據結構的接口。Collection接口是容器類的根接口之一,是各類容器接口的父接口。它的包結構是java.util.Collection,它是接口而不是類。
(2)**Collections是一個集合或者容易操作的工具類。它是一個實現類,不是一個接口。**它是一個工具類,它的方法都是靜態方法,用於操作各個容器類。它的包結構是java.util.Collections.

Java的類命名有這樣一個特點,如果一個類名後帶s,基本上都是輔助的工具類,
比如容器的輔助工具類Collections,數組的輔助工具類Arrays, 對象工具類Objects, 它們的方法都是靜態方法。

面試題2 簡述Collection和Collection的區別

  • (1)Collection接口
    **Collection接口是容器類的頂層接口之一,**它的子接口主要有List和Set,所有實現了List或Set接口的容器類,如ArrayList、 LinkedList、HashSet 和TreeSet等,同時也實現了Collection接口。Collection接口定義了這些容器類操作容器元素的基本方法, 比如向某個容器中添加元素、刪除元素、遍歷元素等。
  • (2)Collections類
    Collections是一個容器框架的工具類,該工具類中的方法都是靜態方法,可通過類名直接調用。 通過這些方法可對容器中的元素進行排序、查找、求最大值等輔助操作,因此Collections是容器類中最重要的輔助工具類。

2 HashSet 和 TreeSet

知識點梳理

  • (1) HashSet
    HashSet是java.util包中的類,實現了Set接口,封裝了HashMap, 元素是通過HashMap來保存的。
    關於HashSet有以下幾點需要補充說明:
    1)HashSet中的元素可以是null,但只能有一個null(因爲實現了Set接口,所以不允許有重複的值)。
    2)HashSet是非線程安全的。
    3)插入HashSet中的對象不保證與插入的順序一致,元素的排列順序可能改變
    4)向HashSet中添加新的對象時,HashSet類會進行重複對象元素判斷;判斷添加對象和容器內已有對象是否重複,如果重複則不添加,如果不重複則添加。

  • (2)TreeSet
    TreeSet是java.util包中的類,也實現了Set接口,因此TreeSet中同樣不能有重複元素。TreeSet封裝了TreeMap,所以是一個有序的容器,容器內的元素是排好序的。
    關於TreeSet,有以下幾點需要補充說明:
    1)TreeSet中的元素是一個有序的集合(在插入元素時會進行排序),不允許放入null值
    2)TreeSet是非線程安全的
    3)向TreeSet中添加新的對象時,TreeSet會將添加對象和已有對象進行比較,存在重複對象則不進行添加,不存在重讀對象的情況下,新插入對象和已有對象根據比較結果排序再進行存儲。

面試題2 Set接口的實現類

Set接口的實現類有哪些?HashSet、TreeSet和LinkedHashSet的區別是什麼?TreeSet如何保證有序序列?

  • (1) Set接口的主要實現類
    Set接口的主要實現類包括HashSet、TreeSet、LinkedHashSet、AbstractSet等。
    HashSet是java.util包中的類,它只能存儲不重複的對象。HashSet類實現了Iterable、Set等接口,底層使用HashMap來保存數據,子類有LinkedHashSet等。
    TreeSet是AbstractSet的子類,是一個有序的集合,TreeSet是基於TreeMap來實現。
    LinkedHashSet類是HashMap的子類,它的元素也是唯一的,LinkedHashSet是基於HashMap和雙向鏈表的實現類。
    AbstractSet類是一個抽象類,同時實現了Collection 和Set接口,HashSet和TreeSet都是它的子類。

  • (2) HashSet、TreeSet和LinkedHashSet之間的主要區別
    1)HashSet是基於哈希表HashMap來實現的,它包含的是不保證有序的不重複的元素。
    2)TreeSet是基於TreeMap來實現的,而TreeMap基於紅黑樹算法實現,紅黑樹是一種平衡的排序二叉樹,它包含的是有序且不重複的元素。TreeSet支持兩種排序方式:自然排序和定製排序。
    3)LinkedHashSet繼承自HashSet,與HashSet相比,它底層是用雙向鏈表實現,用鏈表記錄數據,實現了按照插入的順序有序,也就是說,遍歷序和插入序是一致的。LinkedHashSet在迭代訪問Set中全部元素時性能會比HashSet好,但插入元素時性能不如HashSet。

  • (3) TreeSet 保證有序的方式
    TreeSet底層數據結構是一種自平衡的二叉樹,即紅黑樹,紅黑樹保證了元素的有序性,無論按照前序、中序、後序都可以有序的讀取集合中的元素,也就是說,按照紅黑樹的結點進行存儲和取出數據。

3 ArrayList、Vector和LinkedList

知識點梳理

List是用於存放多個元素的容器,它允許有重複的元素,並保證元素之間的先後順序。List有3個主要的實現類:ArrayList、Vector和LinkedList。

  • (1) ArrayList
    ArrayList類又稱爲動態數組,該容器類實現了列表的相關操作。ArrayList的內部結構由數組實現,因此可對容器內元素實現快速隨機訪問。但因爲在ArrayList中插入或刪除一個元素需要移動其他元素,所以不適合在插入和刪除頻繁的場景下使用ArrayList。與此同時,ArrayList的容量可以隨着元素的增加而自動增加,所以不用擔心ArrayList容量不足的問題。另外ArrayList是非線程安全的。

  • (2) Vector
    Vector類又稱爲向量類,也實現了類似動態數組的功能,內部數據結構也由數組實現。與ArrayList不同的是,Vector是線程安全的,它的方法都是同步方法,所以訪問效率低於ArrayList,另外Vector是非泛型集合,可以往其中隨意插入不同類的對象,不需要考慮類型和預先選定向量的容量,可方便的進行查找等操作。當然也可以使用Vector的泛型取代非泛型類型(如Vectort)。

  • (3)LinkedList
    LinkedList也是List的一個重要實現類。它的內部數據結構由鏈表實現、並且是非線程安全的,適合數據的動態插入和刪除,插入和刪除元素時不需要對數據進行移動,所以插入、刪除效率高,但隨機訪問速度較慢。

面試題1 ArrayList和LinkedList

  • (1)ArrayList 擴容規則
    ArrayList類又稱爲動態數組,內部數據結構由數組實現,數組的容量可以自動增長,當數組容量不足以存放新增元素時,需要進行數組的擴容,擴容的基本策略如下:
    每次向數組中添加元素時,要檢查添加元素後的容量是否超過當前數組的長度,如果沒有超過,則添加該元素,不做其他操作;如果超過,每次擴充原容量的1.5倍。

(2)ArrayList和LinkedList 的區別
第一:首先ArrayList和LinkedList都實現了List接口,同時LinkedList也實現Deque接口,這樣能將LinkedList當作雙端隊列使用。
Deque接口的定義如下:

public interface Deque<E> extends Queue<E>

Deque 繼承Queue接口,所以LinkedList也實現了Queue接口

第二:ArrayList是可改變大小的數組,底層由數組實現,能夠自動擴容。LinkedList是雙向鏈接隊列,底層由鏈表來實現。所以說,ArryaList 是可改變大小的數組,而LinkedList是雙向鏈接隊列。

第三:ArrayList是基於數組結果實現的,數組具備高速隨機訪問的特點,所以ArrayList支持高校的隨機元素訪問。
LinkedList是基於鏈表結構實現的,如果想返回某個元素的位置,必須從前往後遍歷鏈表,所以LinkedList不支持高效的隨機訪問。

第四:LinkedList是鏈式存儲結構,所以插入和刪除元素效率較高,因此不需要移動元素。
ArrayList是數組存儲結構,所以插入和刪除操作都會引起後續元素移動,而且在容量不足時還存在擴容問題。

面試題2 簡述ArrayList和Vector 的區別

ArrayList和Vector 的共同點:ArrayList和Vector 都是List的主要實現類,內部數據結構都用數組來實現,都允許對元素快速隨機訪問。
除此之外, ArrayList和Vector 也存在很多的區別,總結起來有以下幾點:

  • (1) 線程安全:
    Vector類是線程安全類,大部分方法都是痛不的,而ArrayList是非線程安全的。所以Vector有較大的系統開銷,ArrayList在性能上優於Vector。

  • (2) 容量擴展機制:
    當需要擴容時,ArrayList和Vector 都可以進行自動容量擴展。ArrayList擴展後數組的大小爲原數組長度的1.5倍,同時ArrayList可以用構造函數指定數組容量的大小。對於Vector,當它的擴容因子大於0時,新數組長度爲原數組長度+擴容因襲,否則擴展後的數組大小爲原數組的2倍。

  • (3)類成員屬性
    ArrayList有2個屬性,即存儲數據的數組elementData、存儲記錄元素個數的size。Vector有3個屬性,即存儲數據的數組elementData、存儲記錄元素個數的size,同時還有擴展數組大小的擴展因子capacityIncrement。

4 HashMap 和 Hashtable

HashMap 和 Hashtable是Map接口的主要實現類。

  • HashMap 類
    (1) HashMap 的概念和特點
    HashMap 又稱爲哈希表,它是根據鍵key的hashCode值來存儲數據的。它存儲的是鍵值對(key- value)映射,具有快速定位的特點。HashMap 繼承於AbstractMap, 實現了Map等接口。它的實現是不同步的,因此不是線程安全的。它的key、value都可以爲null,而key最多隻能出現一個null。同時HashMap中的映射不是有序的(存儲不等於插入序)。
    (2)HashMap 的數據結構
    HashMap 的數據結構是由數組+鏈表+紅黑樹來實現的。HashMap 底層是一個數組Entry[] table,數組中的每個元素Entry都是一個單向鏈表的引用,從JDK1.8開始,當鏈表長度大於8時,鏈表會調整爲紅黑樹結構。
    (3)HashMap 對象的兩個重要屬性和擴容
    HashMap 對象有兩個重要的屬性:初始容量和加載因子。
    初始容量是指HashMap 在創建時的容量,加載因子是HashMap 在其容量自動增加之前可以達到多滿的一種尺度。
    HashMap 的初始容量默認值爲16,默認加載因子是0.75,當HashMap 中的元素數目超出加載因子與當前容量的乘積時,則要對該HashMap 進行擴容操作,擴容後數組大小爲當前的2倍。
    (3)HashMap 的衝突管理
    HashMap 採用“hash算法”來決定每個元素的存儲位置,當添加新的元素時,系統會調用hashCode()方法得到一個hashCode值,再根據這個hashCode值決定這個元素在HashMap中的存儲位置。當不同的對象的hashCode值相同時,就出現了衝突。
    HashMap 採用鏈地址法,即用單鏈表將所有衝突的元素鏈接起來,通過這種方法來進行衝突管理。當鏈表的元素個數大於8時,會自動轉爲紅黑樹結構,這樣會提升查詢性能,把順序搜索鏈表記錄的時間複雜度從O(n)提高到O(logn)。

  • Hashtable類
    Hashtable與HahMap類似,不同的是Hashtable是線程安全的,而且屬於遺留類。
    需要注意的是,如果對同步性和遺留代碼的兼容性沒有特殊要求,建議使用HahMap類,這是因爲Hashtable雖然由線程安全的特點,但是效率較低。

HashMap和 Hashtable是Map接口的兩個重要實現類,HashMap是Hashtable的輕量級實現。
HashMap是非線程安全的,HashMap的鍵(key) 和 值(value)都支持null。
Hashtable是線程安全的, Hashtable鍵(key)和值(value)都不支持null。

面試題2 HashMap爲什麼要引入紅黑樹結構

這道題目是目前HashMap筆試面試中考察拼了最高的題目之一,幾乎是逢考必有的題目。

HashMap 採用數組和鏈表相結合的數據結構,底層是一個數組,每個數組元素都是一個鏈表結構,鏈表的每個節點就是HashMap中每個元素(鍵值對)。當要向HashMap在添加一個鍵值對時,會先調用該鍵值對的key的hashCode()方法計算出hashCode值,從而得到該元素在數組中的下標。如果數組在該位置上已保存有元素(已存在一個鏈表),則說明發生了衝突(不同的key值對應了同一個hash值,所以映射的數組下標也相同),接下來就要按照HashMap衝突管理算法進行處理。

HashMap採用鏈地址法,即用單鏈表將所有衝突的元素鏈接起來,通過這種方法來進行衝突管理。但是這個鏈表並不會無限的增長,當立案標準元素個數大於8時,這個鏈表會自動轉爲紅黑樹結構。
之所以引入紅黑樹結構是因爲在鏈表中查找每個元素的時間複雜度都是O(n),而在紅黑樹中查找元素的時間複雜度爲O(logn),這樣當HashMap中元素量較多併產生了大量Hash衝突時,紅黑樹的快速增刪改查的特點能提高HashMap的性能。

紅黑樹(Red Black Tree) 是一種自平衡二叉查找樹,紅黑樹用紅色和黑色來標記節點,並且有以下三個特點:
1) 根和葉子結點都是黑色的
2)從每個葉子都根的所有路徑上不能有兩個連續的紅色結點
3)從任一結點到它所能到達的葉子結點的所有簡單路徑都包含相同數目的黑色結點。
以上三個特徵保證了紅黑樹比其他的二叉查找樹有更好的結點查找穩定性、查找效率和增刪結點的效率。

鑑於以上原因,引入了紅黑樹來解決HashMap的哈希衝突效率等問題。

問題:紅黑樹這麼好?爲什麼在元素個數小於8個時還要用鏈表,而不直接使用紅黑樹?

回答:當元素數目較少時,鏈表的效率更高,而紅黑樹的實現和調整都更復雜,反而會影響整體性能。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章