Java Collections框架是Java編程語言的核心API之一。java
這是Java面試問題的重要主題之一。在這裏,我列出了一些重要的Java集合面試問題和解答,以幫助您進行面試。這直接來自我14年以上的Java編程經驗。程序員
Java 8對 Collection API 進行了重大更改。一些更改是:面試
Iterator
接口中的forEachRemaining(Consumer action)
,MapreplaceAll()
,compute()
,merge()
方法。每一個編程語言都使用集合,和最初的Java版本包含幾個集合類:Vector,Stack,Hashtable和Array。可是從較高的範圍和用法來看,Java 1.2提出了Collections Framework,該框架將全部collections接口,實現和算法分組。算法
Java的集合經過使用泛型和併發集合類進行線程安全操做已經走了很長一段路。它還包括在Java的併發包中的阻塞接口及其實現。數據庫
Collections 框架的一些好處是;編程
Java 1.5帶有泛型,全部集合接口和實現都大量使用它。泛型容許咱們提供集合能夠包含的Object的類型,所以,若是您嘗試添加其餘類型的任何元素,則引起編譯時錯誤。數組
這樣能夠避免在運行時發生ClassCastException,由於您將在編譯時收到錯誤。因爲咱們不須要使用強制轉換和_實例化_運算符,所以泛型也使代碼更乾淨。緩存
Collection 表示集合層次結構的根。Collection表示一組元素的對象。Java平臺不提供此接口的任何直接實現。安全
Set是一個不能包含重複元素的集合。此接口對數學集合的抽象進行建模,並表示集合,例如紙牌集合。併發
List是一個有序的集合,能夠包含重複的元素。您能夠從其索引訪問任何元素。該列表更像是具備動態長度的數組。
一個Map是鍵映射到值的對象。映射不能包含重複的鍵:每一個鍵最多能夠映射到一個值。
其餘一些接口Queue
,Dequeue
,Iterator
,SortedSet
,SortedMap
和ListIterator
。
Collection接口指定爲一組元素對象。元素的維護方式取決於Collection的具體實現。例如,某些Collection實現(例如List)容許重複元素,而其餘實現(例如Set)則不容許重複元素。
許多Collection實現都有Cloneable方法。可是,將其包含在Collection的全部實現中沒有意義。這是由於Collection是抽象表示。重要的是。
在處理實際實現時,克隆或序列化的語義及其含義都會發揮做用。所以具體的實現應決定如何克隆或序列化它,甚至能夠對其進行克隆或序列化。
所以,在全部實現中強制進行克隆和序列化的靈活性較差,限制也更大。具體實現應決定是否能夠克隆或序列化。
儘管Map接口及其實現是Collections Framework的一部分,但Map不是集合,集合也不是Map。所以,Map擴展Collection是沒有意義的,反之亦然。
若是Map擴展了Collection接口,那麼元素在哪裏?該映射包含key-value對,而且提供了一些方法來檢索鍵或值的列表做爲Collection,但它不適合「元素組」範式。
迭代器接口提供了對任何Collection進行迭代的方法。咱們可使用_iterator()_方法從Collection中獲取迭代器實例。在Java Collections Framework中,迭代器代替了枚舉。迭代器容許調用者在迭代過程當中從基礎集合中刪除元素。Java Collection迭代器提供了遍歷集合元素的通用方法,並實現了Iterator Design Pattern。
枚舉的速度是Iterator的兩倍,而且使用的內存更少。枚舉是很是基本的,適合基本需求。可是,與Enumeration相比,Iterator安全得多,由於它始終拒絕其餘線程修改被其迭代的集合對象。
在Java Collections Framework中,迭代器代替了枚舉。迭代器容許調用者從基礎集合中刪除Enumeration沒法實現的元素。迭代器方法名稱已獲得改進,以使其功能更清晰。
考慮到Iterator的約定不保證迭代順序,緣由尚不清楚。可是請注意,ListIterator確實提供了add操做,由於它確實保證了迭代的順序。
能夠在當前Iterator接口的頂部實現它,可是因爲不多使用它,所以將它包含在每一個人都必須實現的接口中沒有意義。
咱們能夠經過兩種不一樣的方式遍歷列表-使用迭代器和使用for-each循環。
List <String> strList = new ArrayList<>(); for(String obj:strList){ System.out.println(obj); } Iterator<String> it= strList.iterator(); while(it.hasNext()){ String obj = it.next(); System.out.println(obj); }
使用迭代器更加線程安全,由於它能夠確保若是基礎列表元素被修改,它將拋出異常ConcurrentModificationException
。
每當咱們嘗試獲取下一個元素時,迭代器fail-fast屬性都會檢查基礎集合的結構是否有任何修改。若是找到任何修改,則拋出ConcurrentModificationException
。除了並行併發類(例如ConcurrentHashMap和CopyOnWriteArrayList)以外,Collection類中Iterator的全部實如今設計上都是fail-fast的。
Iterato fail-safe屬性可與基礎集合的克隆一塊兒使用,所以不受集合中任何修改的影響。按照設計,java.util
包中的全部集合類都是fail-fast的,而其中的集合類java.util.concurrent
是fail-safe的。
fail-fast迭代器會拋出ConcurrentModificationException,而fail-safe迭代器毫不會拋出ConcurrentModificationException。
咱們可使用併發集合類來避免ConcurrentModificationException
在集合上進行迭代,例如使用CopyOnWriteArrayList而不是ArrayList。
Iterator接口聲明瞭用於迭代集合的方法,可是其實現是Collection實現類的責任。每一個返回迭代器以進行遍歷的集合類都有其本身的Iterator實現嵌套類。
這使集合類能夠選擇迭代器是fail-fast仍是fail-safe的。例如,ArrayList迭代器是fail-fast的,而CopyOnWriteArrayList迭代器是fail-safe的。
UnsupportedOperationException
是用於指示不支持該操做的異常。它普遍用於在JDK類,在集合框架java.util.Collections.UnmodifiableCollection
拋出該異常全部add
和remove
操做。
HashMap在Map.Entry
靜態嵌套類實現中存儲鍵值對。HashMap使用哈希算法,並在put
和get
方法中使用hashCode()和equals()方法。
當咱們put
經過傳遞鍵值對來調用方法時,HashMap使用帶有哈希值的Key hashCode()來查找存儲鍵值對的索引。該條目存儲在LinkedList中,所以,若是已經存在一個條目,則使用equals()方法檢查傳遞的鍵是否已存在,若是是,它將覆蓋該值,不然它將建立一個新條目並存儲此鍵值條目。
當咱們get
經過傳遞Key來調用method時,它再次使用hashCode()在數組中找到索引,而後使用equals()方法找到正確的Entry並返回其值。下圖將清楚地解釋這些細節。
有關HashMap的其餘重要信息是容量,負載因子,閾值大小調整。HashMap的初始默認容量爲16,負載係數爲0.75。閾值是容量乘以負載因子,而且若是Map大小大於閾值,則每當咱們嘗試添加條目時,HashMap都會將Map的內容從新映射爲容量更大的新數組。容量始終是2的乘方,所以,若是您知道須要存儲大量的鍵值對,例如在緩存數據庫中的數據時,最好使用正確的容量和負載因子來初始化HashMap。 。
HashMap使用Key對象的hashCode()和equals()方法來肯定放置鍵值對的索引。當咱們嘗試從HashMap中獲取價值時,也會使用這些方法。若是這些方法的實現不正確,則兩個不一樣的Key可能會產生相同的hashCode()和equals()輸出,在這種狀況下,HashMap不會考慮將它們存儲在不一樣的位置,而是將其覆蓋並覆蓋它們。
一樣,全部不存儲重複數據的集合類都使用hashCode()和equals()查找重複項,所以正確實現它們很是重要。equals()和hashCode()的實現應遵循如下規則。
o1.equals(o2)
,那麼o1.hashCode() == o2.hashCode()
應該永遠如此true
。o1.hashCode() == o2.hashCode
是真的,這並不意味着o1.equals(o2)
會true
。咱們能夠將任何類用做Map Key,可是在使用它們以前應考慮如下幾點。
用戶定義的鍵類的最佳實踐是使其不可變,以即可以將hashCode()值緩存起來以提升性能。不可變的類還確保hashCode()和equals()未來不會更改,這將解決任何可變性問題。
例如,假設我有一個MyKey
用於HashMap鍵的類。
//傳遞的mykey name參數用於equals()和hashcode() MyKey key = new MyKey("Pankaj"); //假定hashCode=1234 myHashMap.put(key, "Value"); // 下面的代碼將更改equals()和hashcode()的key // 可是它的位置不會改變 key.setName("Amit"); //假定新的hashCode=7890 //下面將返回null,由於HashMap將嘗試查找鍵 //與存儲在同一索引中,但因爲密鑰發生了變化, //不匹配,返回空。 myHashMap.get(new MyKey("Pankaj"));
這就是爲何String和Integer大多用做HashMap鍵的緣由。
Map接口提供了三個集合視圖:
「不積跬步,無以致千里」,但願將來的你能:有夢爲馬 隨處可棲!加油,少年!
關注公衆號:「Java 知己」,天天更新Java知識哦,期待你的到來!