《面試知識,工做可待:集合篇》-java集合面試知識大全

歡迎關注掘金:【Ccww】,一塊兒學習
提高能力,漲薪可待
面試知識,工做可待
實戰演練,拒絕996
也歡迎關注微信公衆號【Ccww筆記】,原創技術文章第一時間推出
若是此文對你有幫助、喜歡的話,那就點個讚唄,點個關注唄!java

前言

是否是感受在工做上難於晉升了呢?
是否是感受找工做面試是那麼難呢?
是否是感受本身天天都在996加班呢?面試

在工做上必須保持學習的能力,這樣才能在工做獲得更好的晉升,漲薪指日可待,歡迎一塊兒學習【提高能力,漲薪可待】系列redis

在找工做面試應在學習的基礎進行總結面試知識點,工做也指日可待,歡迎一塊兒學習【面試知識,工做可待】系列算法

最後,理論知識到準備充足,是否是該躬行起來呢?歡迎一塊兒學習【實戰演練,拒絕996】系列spring

1、集合基礎

1.1 集合框架有哪些優勢以下:

  • 使用核心集合類下降開發成本,而非實現咱們本身的集合類。
  • 隨着使用通過嚴格測試的集合框架類,代碼質量會獲得提升。
  • 經過使用 JDK 附帶的集合類,能夠下降代碼維護成本。
  • 複用性和可操做性。

1.2 Java集合類框架的基本接口有哪些?

Java 集合類提供了一套設計良好的支持對一組對象進行操做的接口和類。Java集合類裏面最基本的接口有:mongodb

  • Collection:表明一組對象,每個對象都是它的子元素。
  • Set:不包含重複元素的 Collection。
  • List:有順序的 collection,而且能夠包含重複元素。
  • Map:能夠把鍵(key)映射到值(value)的對象,鍵不能重複。
  • 還有其它接口 Queue、Dequeue、SortedSet、SortedMap 和 ListIterator。

1.3 爲何集合類沒有實現 Cloneable 和 Serializable 接口?

集合類接口指定了一組叫作元素的對象。集合類接口的每一種具體的實現類均可以選擇以它本身的方式對元素進行保存和排序,可使得集合類很靈活,能夠實現自定義集合類屬性,好比有的集合類容許重複的鍵,有些不容許。編程

1.4 集合框架中的泛型有什麼優勢?

Java5 引入了泛型,全部的集合接口和實現都大量地使用它。泛型容許咱們爲集合提供一個能夠容納的對象類型。所以,若是你添加其它類型的任何元素,它會在編譯時報錯。這避免了在運行時出現 ClassCastException,由於你將會在編譯時獲得報錯信息。數組

泛型也使得代碼整潔,咱們不須要使用顯式轉換和 instanceOf 操做符。它也給運行時帶來好處,由於不會產生類型檢查的字節碼指令。安全

1.5 Collection 和 Collections 的區別?

  • Collection ,是集合類的上級接口,繼承與他的接口主要有 Set 和List 。
  • Collections ,是針對集合類的一個工具類,它提供一系列靜態方法實現對各類集合的搜索、排序、線程安全化等操做。

1.6 什麼是迭代器(Iterator)?

Iterator 接口提供了不少對集合元素進行迭代的方法。每個集合類都包含了能夠返回迭代器實例的 迭代方法。迭代器能夠在迭代的過程當中刪除底層集合的元素。性能優化

克隆(cloning)或者是序列化(serialization)的語義和含義是跟具體的實現相關的。所以,應該由集合類的具體實現來決定如何被克隆或者是序列化。

1.7 Iterator和ListIterator的區別是什麼?

下面列出了他們的區別:

  • Iterator 可用來遍歷 Set 和 List 集合,可是 ListIterator 只能用來遍歷 List 。
  • Iterator 對集合只能是前向遍歷,ListIterator 既能夠前向也能夠後向。
  • ListIterator 實現了 Iterator 接口,幷包含其餘的功能,好比:增長元素,替換元素,獲取前一個和後一個元素的索引,等等。

1.8 快速失敗(fail-fast)和安全失敗(fail-safe)的區別是什麼?

Iterator 的安全失敗是基於對底層集合作拷貝,所以,它不受源集合上修改的影響。

java.util 包下面的全部的集合類都是快速失敗的,而 java.util.concurrent 包下面的全部的類都是安全失敗的。快速失敗的迭代器會拋出 ConcurrentModificationException 異常,而安全失敗的迭代器永遠不會拋出這樣的異常。

1.9 Enumeration 接口和 Iterator 接口的區別有哪些?

Enumeration 速度是 Iterator 的2倍,同時佔用更少的內存。可是,Iterator 遠遠比 Enumeration 安全,由於其餘線程不可以修改正在被 iterator 遍歷的集合裏面的對象。同時,Iterator 容許調用者刪除底層集合裏面的元素,這對 Enumeration 來講是不可能的。

1.10 Java集合類框架的最佳實踐有哪些?

根據應用的須要正確選擇要使用的集合的類型對性能很是重要,好比:假如元素的大小是固定的,並且能事先知道,咱們就應該用 Array 而不是 ArrayList。 有些集合類容許指定初始容量。所以,若是咱們能估計出存儲的元素的數目,咱們能夠設置初始容量來避免從新計算 hash 值或者是擴容。

爲了類型安全,可讀性和健壯性的緣由老是要使用泛型。同時,使用泛型還能夠避免運行時的 ClassCastException。

使用 JDK 提供的不變類(immutable class)做爲Map的鍵能夠避免爲咱們本身的類實現 hashCode() 和 equals() 方法。

編程的時候接口優於實現。

底層的集合其實是空的狀況下,返回長度是0的集合或者是數組,不要返回 null。

2、HashMap、Hashtable

2.1 HashMap的結構(jdk1.8)

HashMap(數組+鏈表+紅黑樹)的結構,利用了紅黑樹,因此其由 數組+鏈表+紅黑 樹組成:

HashMap 裏面是一個數組,而後數組中每一個元素是一個單向鏈表。上圖中,每一個綠色 的實體是嵌套類 Entry 的實例, Entry 包含四個屬性: key, value, hash 值和用於單向鏈表的 next。

2.2 Java 中的 HashMap

HashMap 根據鍵的 hashCode 值存儲數據,大多數狀況下能夠直接定位到它的值,於是具備很快 的訪問速度,但遍歷順序倒是不肯定的。

HashMap 最多隻容許一條記錄的鍵爲 null,容許多條記 錄的值爲 null。

HashMap 非線程安全,即任一時刻能夠有多個線程同時寫 HashMap,可能會導 致數據的不一致。若是須要知足線程安全,能夠用 Collections 的 synchronizedMap 方法使 HashMap 具備線程安全的能力,或者使用 ConcurrentHashMap。

2.3 HashMap重要參數

  1. capacity:當前數組容量16 ,始終保持 2^n,能夠擴容,擴容後數組大小爲當前的 2 倍。
  2. loadFactor:負載因子,默認爲 0.75
  3. threshold:擴容的閾值,等於 capacity * loadFactor

2.4 HashMap查詢

查找的時候,根據 hash 值咱們可以快速定位到數組的 具體下標,可是以後的話, 須要順着鏈表一個個比較下去才能找到咱們須要的,時間複雜度取決 於鏈表的長度,爲 O(n)。

爲了下降這部分的開銷,在 Java8 中, 當鏈表中的元素超過了 8 個之後, 會將鏈表轉換爲紅黑樹,在這些位置進行查找的時候能夠下降時間複雜度爲 O(logN)。

2.5 hashCode() 和 equals() 方法的重要性體如今什麼地方?

Java 中的 HashMap 使用 hashCode() 和 equals() 方法來肯定鍵值對的索引,當根據鍵獲取值的時候也會用到這兩個方法。若是沒有正確的實現這兩個方法,兩個不一樣的鍵可能會有相同的 hash 值。

所以,可能會被集合認爲是相等的。並且,這兩個方法也用來發現重複元素。因此這兩個方法的實現對 HashMap 的精確性和正確性是相當重要的。

2.6 Hashtable

哈希表(HashTable)又叫作散列表,根它經過把key映射到表中一個位置來訪問記錄,以加快查找速度。這個映射函數就叫作散列(哈希)函數,存放記錄的數組叫作散列表。

哈希表是一個時間和空間上平衡的例子。若是沒有空間的限制,咱們能夠直接用鍵來做爲數組的索引,這樣能夠將查找時間作到最快(O(1))。若是沒有時間的限制,咱們可使用無序鏈表進行順序查找,這樣只須要不多的內存

2.7 爲何Hashtable的速度快?

Hashtable是由數組與鏈表。數組的特色就是查找容易,插入刪除困難;而鏈表的特色就是查找困難,可是插入刪除容易。既然二者各有優缺點,那麼Hashtable查找容易,插入刪除也會快起來。

2.8 Hashtable如何根據key查找?

使用哈希函數將被查找的key轉化爲數組的索引。在理想的狀態下,不一樣的鍵會被轉化成不一樣的索引值。可是那是理想狀態,咱們實踐當中是不可能一直是理想狀態的。當不一樣的鍵生成了相同的索引的時候,便是哈希衝突,處理衝突方式:

  • 拉鍊法
  • 線性探索法

2.9 LinkHashMap

LinkHashMapshi=HashMap + LinkedList

LinkedHashMap 是基於 HashMap 實現的一種集合,具備 HashMap 集合上面所說的全部特色,除了 HashMap 無序的特色,LinkedHashMap 是有序的,由於 LinkedHashMap 在 HashMap 的基礎上單獨維護了一個具備全部數據的雙向鏈表,該鏈表保證了元素迭代的順序。

  • LinkedHashMap是繼承於HashMap,是基於HashMap和雙向鏈表來實現的。
  • HashMap無序;LinkedHashMap有序,可分爲插入順序和訪問順序兩種。若是是訪問順序,那put和get操做已存在的Entry時,都會把Entry移動到雙向鏈表的表尾(實際上是先刪除再插入)。
  • LinkedHashMap存取數據,仍是跟HashMap同樣使用的Entry[]的方式,雙向鏈表只是爲了保證順序。
  • LinkedHashMap是線程不安全的

3、ArrayList、 Vector 和 LinkedList

3.1 ArrayList(數組)

ArrayList 是最經常使用的 List 實現類,內部是經過數組實現的,它容許對元素進行快速隨機訪問。

數組的缺點是每一個元素之間不能有間隔, 當數組大小不知足時須要增長存儲能力,就要將已經有數 組的數據複製到新的存儲空間中。

當從 ArrayList 的中間位置插入或者刪除元素時,須要對數組進 行復制、移動、代價比較高。所以,它適合隨機查找和遍歷,不適合插入和刪除。

ArrayList支持序列化功能,支持克隆(淺拷貝)功能,排序功能等

3.2 ArrayList 是如何擴容的?

若是經過無參構造的話,初始數組容量爲 0 ,當真正對數組進行添加時,才真正分配容量。每次按照 1.5 倍(位運算)的比率經過 copeOf 的方式擴容

在 JKD6 中實現是,若是經過無參構造的話,初始數組容量爲10,每次經過 copeOf 的方式擴容後容量爲原來的 1.5 倍

3.3 ArrayList 集合加入 1 萬條數據,應該怎麼提升效率?

ArrayList 的默認初始容量爲 10 ,要插入大量數據的時候須要不斷擴容,而擴容是很是影響性能的。所以,如今明確了 10 萬條數據了,咱們能夠直接在初始化的時候就設置 ArrayList 的容量!

3.4 Vector(數組實現、 線程同步)

Vector 與 ArrayList 同樣,也是經過數組實現的,不一樣的是它支持線程的同步,即某一時刻只有一 個線程可以寫 Vector,避免多線程同時寫而引發的不一致性,但實現同步須要很高的花費,所以, 訪問它比訪問 ArrayList 慢。

3.5 LinkList(鏈表)

LinkedList 是用鏈表結構存儲數據的,很適合數據的動態插入和刪除,隨機訪問和遍歷速度比較 慢。另外,他還提供了 List 接口中沒有定義的方法,專門用於操做表頭和表尾元素,能夠看成堆 棧、隊列和雙向隊列使用

4、HashSet、TreeSet以及LinkHashSet

Set 注重獨一無二的性質,該體系集合用於存儲無序(存入和取出的順序不必定相同)元素, 值不能重複。

對象的相等性本質是對象 hashCode 值( java 是依據對象的內存地址計算出的此序號) 判斷的, 若是想要讓兩個不一樣的對象視爲相等的,就必須覆蓋 Object 的 hashCode 方法和 equals 方法。

4.1 HashSet

哈希表邊存放的是哈希值。 HashSet 存儲元素的順序並非按照存入時的順序(和 List 顯然不一樣) 而是按照哈希值來存的因此取數據也是按照哈希值取得。元素的哈希值是經過元素的hashcode 方法來獲取的, HashSet 首先判斷兩個元素的哈希值,若是哈希值同樣,接着會比較equals 方法 若是 equls 結果爲 true , HashSet 就視爲同一個元素。若是 equals 爲 false 就不是同一個元素。

HashSet 經過 hashCode 值來肯定元素在內存中的位置。 一個 hashCode 位置上能夠存放多個元素。

哈希值相同 equals 爲 false 的元素是怎麼存儲呢,就是在一樣的哈希值下順延(能夠認爲哈希值相同的元素放在一個哈希桶中)。也就是哈希同樣的存一列。 如圖 1 表示 hashCode 值不相同的狀況; 圖 2 表示 hashCode 值相同,但 equals 不相同的狀況。

4.2 TreeSet(二叉樹)

  1. TreeSet()是使用二叉樹的原理對新add()的對象按照指定的順序排序(升序、降序),每增長一個對象都會進行排序,將對象插入的二叉樹指定的位置。
  2. Integer 和 String 對象均可以進行默認的 TreeSet 排序,而自定義類的對象是不能夠的, 本身定義的類必須實現 Comparable 接口,而且覆寫相應的 compareTo()函數,才能夠正常使用。
  3. 在覆寫 compare()函數時,要返回相應的值才能使 TreeSet 按照必定的規則來排序
  4. 比較此對象與指定對象的順序。若是該對象小於、等於或大於指定對象,則分別返回負整數、零或正整數

4.3 LinkHashSet( HashSet+LinkedHashMap)

對於 LinkedHashSet 而言,它繼承與 HashSet、又基於 LinkedHashMap 來實現的。LinkedHashSet 底層使用 LinkedHashMap 來保存全部元素,它繼承與 HashSet,其全部的方法操做上又與 HashSet 相同.

所以 LinkedHashSet的實現上很是簡單,只提供了四個構造方法,並經過傳遞一個標識參數,調用父類的構造器,底層構造一個 LinkedHashMap 來實現,在相關操做上與父類 HashSet 的操做相同,直接調用父類 HashSet 的方法便可。

5、集合的區別

5.1 HashMap 和 Hashtable 有什麼區別?

HashMap 和 Hashtable 都實現了 Map 接口,所以不少特性很是類似。可是,他們有如下不一樣點: HashMap 容許鍵和值是 null,而 Hashtable 不容許鍵或者值是 null。

Hashtable 是同步的,而 HashMap 不是。所以, HashMap 更適合於單線程環境,而 Hashtable 適合於多線程環境。

HashMap 提供了可供應用迭代的鍵的集合,所以,HashMap 是快速失敗的。另外一方面,Hashtable 提供了對鍵的列舉(Enumeration)。

通常認爲 Hashtable 是一個遺留的類。

5.2 數組(Array)和列表(ArrayList)有什麼區別?何時應該使用 Array 而不是 ArrayList?

下面列出了 Array 和 ArrayList 的不一樣點:

Array 能夠包含基本類型和對象類型,ArrayList 只能包含對象類型。

Array 大小是固定的,ArrayList 的大小是動態變化的。

ArrayList 提供了更多的方法和特性,好比:addAll(),removeAll(),iterator()等等。 對於基本類型數據,集合使用自動裝箱來減小編碼工做量。可是,當處理固定大小的基本數據類型的時候,這種方式相對比較慢。

5.3 ArrayList 和 LinkedList 有什麼區別?

ArrayList 和 LinkedList 都實現了 List 接口,他們有如下的不一樣點:

ArrayList 是基於索引的數據接口,它的底層是數組。它能夠以O(1)時間複雜度對元素進行隨機訪問。與此對應,LinkedList 是以元素列表的形式存儲它的數據,每個元素都和它的前一個和後一個元素連接在一塊兒,在這種狀況下,查找某個元素的時間複雜度是O(n)。

相對於 ArrayList,LinkedList 的插入,添加,刪除操做速度更快,由於當元素被添加到集合任意位置的時候,不須要像數組那樣從新計算大小或者是更新索引。

LinkedList 比 ArrayList 更佔內存,由於 LinkedList 爲每個節點存儲了兩個引用,一個指向前一個元素,一個指向下一個元素。

也能夠參考 ArrayList vs. LinkedList。

5.4 Comparable 和Comparator 接口是幹什麼的?列出它們的區別。

Java 提供了只包含一個 compareTo() 方法的 Comparable 接口。這個方法能夠個給兩個對象排序。具體來講,它返回負數,0,正數來代表輸入對象小於,等於,大於已經存在的對象。

Java 提供了包含 compare() 和 equals() 兩個方法的 Comparator 接口。compare() 方法用來給兩個輸入參數排序,返回負數,0,正數代表第一個參數是小於,等於,大於第二個參數。equals() 方法須要一個對象做爲參數,它用來決定輸入參數是否和 comparator 相等。只有當輸入參數也是一個 comparator 而且輸入參數和當前 comparator 的排序結果是相同的時候,這個方法才返回 true。

5.5 HashSet 和 TreeSet 有什麼區別?

HashSet 是由一個 hash 表來實現的,所以,它的元素是無序的。add(),remove(),contains()方法的時間複雜度是 O(1)。

另外一方面,TreeSet 是由一個樹形的結構來實現的,它裏面的元素是有序的。所以,add(),remove(),contains() 方法的時間複雜度是 O(logn)。

5.6 HashMap 和 ConcurrentHashMap 的區別?

ConcurrentHashMap 是線程安全的 HashMap 的實現。主要區別以下:

  1. ConcurrentHashMap 對整個桶數組進行了分割分段(Segment),而後在每個分段上都用 lock 鎖進行保護,相對 於Hashtable 的 syn 關鍵字鎖的粒度更精細了一些,併發性能更好。而 HashMap 沒有鎖機制,不是線程安全的。

  2. HashMap 的鍵值對容許有 null ,可是 ConCurrentHashMap 都不容許

JDK8 以後,ConcurrentHashMap 啓用了一種全新的方式實現,利用 CAS 算法。

5.7 List、Set、Map 是否繼承自 Collection 接口?

List、Set 是,Map 不是。Map 是鍵值對映射容器,與 List 和 Set 有明顯的區別,而 Set 存儲的零散的元素且不容許有重複元素(數學中的集合也是如此),List 是線性結構的容器,適用於按數值索引訪問元素的情形。

5.8 說出 ArrayList、Vector、LinkedList 的存儲性能和特性?

ArrayList 和 Vector 都是使用數組方式存儲數據,此數組元素數大於實際存儲的數據以便增長和插入元素,它們都容許直接按序號索引娶元素,可是插入元素要涉及數組元素移動等內存操做,因此索引數據快而插入數據慢,Vector 因爲使用了 synchronized 方法(線程安全),一般性能上較 ArrayList 差。

而 LinkedList 使用雙向鏈表實現存儲(將內存中零散的內存單元經過附加的引用關聯起來,造成一個能夠按序號索引的線性結構,這種鏈式存儲方式與數組的連續存儲方式相比,其實對內存的利用率更高),按序號索引數據須要進行前向或後向遍歷,可是插入數據時只須要記錄本項的先後項便可,因此插入速度較快。

Vector 屬於遺留容器(早期的 JDK 中使用的容器,除此以外 Hashtable、Dictionary、BitSet、Stack、Properties 都是遺留容器),如今已經不推薦使用,可是因爲 ArrayList 和 LinkedListed 都是非線程安全的,若是須要多個線程操做同一個容器,那麼能夠經過工具類 Collections 中的 synchronizedList 方法將其轉換成線程安全的容器後再使用(這實際上是裝潢模式最好的例子,將已有對象傳入另外一個類的構造器中建立新的對象來增長新功能)。

5.9 List、Map、Set 三個接口存儲元素時各有什麼特色?

  • List 是有序的 Collection,使用此接口可以精確的控制每一個元素插入的位置。用戶可以使用索引(元素在 List 中的位置,相似於數組下標)來訪問 List 中的元素,這相似於 Java 的數組。
  • Set 是一種不包含重複的元素的 Collection,即任意的兩個元素 e1 和 e2 都有e1.equals(e2)=false,Set 最多有一個 null 元素。
  • Map 接口 :請注意,Map 沒有繼承 Collection 接口,Map 提供 key 到 value 的映射

參考
http://svip.iocoder.cn/Java/Collection/Interview/
https://www.jianshu.com/p/8f4f58b4b8ab

歡迎關注微信公衆號【Ccww筆記】,原創技術文章第一時間推出。

相關文章
相關標籤/搜索