Java集合-----真實大廠面試題彙總(含答案)

面試題1. HashMap的擴容?

面話題2. 一個ArrayList在循環過程中刪除,會不會出問題,爲什麼?

肯定會出現問題的,原因有以下幾個:

  1. 如果要是使用的是 for (int i = 0; i < list.size();i++)這種for循環的話,當我要刪除的這個List中有兩個相同的元素值,而剛好我要刪除這個相同的元素的話,就會導致的結果是隻能刪除掉第一個元素值
    原因:是因爲在List中的API中的remove()方法底層刪除的時候其實是調用了 System.arraycopy(elementData, index+1, elementData, index, numMoved);這個方法(elementData表示當前list,第二個elementData其實表示的是我要複製到的目的List,index+1是當前List的下標起始位置),在刪除第一個元素之後,會把刪除元素後面的所有元素都往前移動,剛好把第二個和第一個相同的元素的值補到剛剛刪除的空位置下,但是繼續遍歷的時候是從剛剛刪除的那個元素下標之後開始,所以剛好把第一個元素和 他相同的第二個元素跳過去
    解決辦法:就是進行倒序刪除元素,就可以避免這種情況的發生。

  2. 如果使用foreach,比如在使用for (String s : list)的時候 ,調用了remove()的話,就會出現java.util.ConcurrentModificationException這個錯誤。
    原因是因爲:foreach寫法是對實際的Iterable、hasNext、next方法的簡寫,問題同樣處在上文的fastRemove方法中,第一句代碼就是modCount++,但是在checkForComodification()方法中會對list中記錄的修改的次數(modCount )和開始期望的list的修改次數(expectedModCount他是初始化的時候就已經固定了 )進行判斷,如果跟預期的list的修改次數不一樣的時候就會報錯。,這裏其實是基於Fail-Fast的機制。

    在一個迭代器初始的時候會賦予它調用這個迭代器的對象的mCount,如何在迭代器遍歷的過程中,一旦發現這個對象的mcount和迭代器中存儲的mcount不一樣那就拋異常

面試題3. HashMap在高併發下如果沒有處理線程安全會有怎樣的安全隱患,具體表現是什麼

  1. 在高併發情況下有可能會形成循環鏈表,具體表現爲CPU的使用率達到100%
  2. 還有一箇中可能是因爲如果兩個線程同時進行對一個節點記性put的時候,可能會出現最後一個put操作會覆蓋第一個put的數據。

面試題4. hashmap和treemap時間複雜度。

首先TreeMap是繼承了AbstractMap實現了SortMap的接口,在構造TreeMap的時候都是直接傳入的是Comparator對象,如果不是的話,就使用默認key的Comparable接口(實現自然排序)

  1. HashMap的時間複雜度爲O(1)
  2. 而TreeMap的時間複雜度爲O(logN),因爲TreeMap的底層是用紅黑樹來實現的,但是他的key不能爲null值,也不可以重複(重複則覆蓋),主要一般使用TreeMap來進行一些排序功能

面試題5. linkedList與arrayList區別 適用場景

面試題6. Arraylist是如何擴容的(默認數組大小是10)?

  1. 首先當你執行add()方法的時候,會先調用這個方法ensureCapacityInternal()來確保數組的容量夠用,然後會繼續在這個方法中調用ensureExplicitCapacity(alculateCapacity(elementData, minCapacity))中的alculateCapacity(elementData, minCapacity)方法來進行比較出是ArrayList中的默認容量和自己初始化的容量那個更大,返回最大的那個作爲最後數組的容量
  2. 把上一個獲得的最終數組最大的容量傳給ensureExplicitCapacity(上一步中最大的值max),然後進行判斷如果這個最大的值大於數組中的元素的長度的話,就說明數組的容量不夠存儲這個數組的元素的大小,然後進行擴容grow()
  3. 在grow()方法中,先進行對之前的數組容量進行1.5倍擴容,然後判斷如果擴容1.5倍後的大小仍然小於之前數組最終那次初始化的容量大小時,說明按此初始化的數組容量大小仍然可以滿足,直接把擴容後的數組大小還是初始化的那個,依然不變,
  4. 如果擴容後的新數組大小大於Integer-8的值的話,反正不能超過Integer的範圍,最後直接調用Arrays.copyOf()方法進行復制數組。
    

面試題7. hashset和hashmap的區別?

  1. HashSet其實底層的構造就是使用的是HashMap來進行實現的,但是還是會有些不一樣的地反
  2. 第一個是HashSet不允許元素重複,就算如果元素重複的話,也不會去覆蓋之前重複的元素。
  3. 還有一點就是在HashSet中存儲的是對象的話,最好需要重寫equals方法和hashCode方法,以保證放入Set對象的唯一性
  4. HashSet不提供get()方法,而且因爲set內部是無序的,所以只能通過迭代器進行遍歷。

面試題8. JDK1.7和JDK1.8的ConcurrentHashMap的區別?

  1. 結構上:JDK1.7中的時候ConcurrentHashMap採用了數組+Segment+分段鎖的方式實現。而1.8使用的是數組+CAS+紅黑樹/鏈表來進行實現的。
  2. 保證線程安全的鎖機制上:JDK1.7採用segment的分段鎖機制實現線程安全,其中segment繼承自ReentrantLock。JDK1.8採用CAS+Synchronized保證線程安全。
  3. 鎖的粒度:原來是對需要進行數據操作的Segment加鎖,現調整爲對每個數組元素加鎖(Node)。
  4. 鏈表轉化爲紅黑樹:定位結點的hash算法簡化會帶來弊端,Hash衝突加劇,因此在鏈表節點數量大於8時,會將鏈表轉化爲紅黑樹進行存儲。
  5. 查詢時間複雜度:從原來的遍歷鏈表O(n),變成遍歷紅黑樹O(logN)。
  6. JDK1.8爲什麼使用內置鎖synchronized來代替重入鎖ReentrantLock,我覺得有以下幾點:
    1. 因爲粒度降低了,在相對而言的低粒度加鎖方式,synchronized並不比ReentrantLock差,在粗粒度加鎖中ReentrantLock可能通過Condition來控制各個低粒度的邊界,更加的靈活,而在低粒度中,Condition的優勢就沒有了
    2. JVM的開發團隊從來都沒有放棄synchronized,而且基於JVM的synchronized優化空間更大,使用內嵌的關鍵字比使用API更加自然
    3. 在大量的數據操作下,對於JVM的內存壓力,基於API的ReentrantLock會開銷更多的內存,雖然不是瓶頸,但是也是一個選擇依據

面試題9. java容器類的層次結構,分別講一下各種容器的原理,LinkedList的刪除操作怎麼實現;TreeSet的add操作怎麼實現的,詳細一點;LinkedHashMap實現機制,如何保證添加元素的有序性。

面試題10. 在hashmap做put的時候是怎麼做hash的以及爲什麼容量是2的n次方

面試題11. 讓你設計個lru,你會考慮哪些因素,具體怎麼做(答:容量還有熱點數據.然後面試官還補充了搜索,我說用linkedhashmap做,然後說了下怎麼實現,他說還有什麼其他方式麼?linkedlist hashmap)

面試題12. Currenthashmap工作原理?

面試題13. 有哪些集合類實現了Map接口?

面試題14. HashMap如果空間利用率不高,怎麼改進

面試題15. hashmap,treemap之間的區別?

面試題16. HashSet和TreeSet的區別?

HashSet底層其實相當於是包了一層HashMap,但是又不同於HashMap,不僅不提供get()方法,因爲它是基於hash表的這樣一種數據結構,在查詢和刪除快,添加比較慢,因爲它是根據存進來的對象的hashcode來進行定位數據,所以如果是添加對象的話,一般建議需要進行重寫hashCode()和equals()方法。

  1. 第一個他的特點就是存儲的數據都是無序的
  2. 第二個就是他存儲的數據不能重複,因爲它是靠對象的hashCode()方法和equals()方法來進行區分重複數據的

而TreeSet的底層其實是包了一層TreeMap的,他之所以能進行對數據的排序是因爲他的底層是使用了二叉樹(這樣的話,當他的數據越多樹的高度越高效率就越低)

  1. TreeSet的特點就是他可以進行排序數據,因爲他是基於Comparable接口來進行區分重複的數據的
  2. 他是基於樹的數據結構的,所以他的查詢刪除、containsValue()的時間複雜度都是O(logN)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章