1. 程式人生 > 實用技巧 >Java面試-集合

Java面試-集合

目錄

  • Collection
    • List
      • ArrayList
      • Vector
      • LinkedList
    • Set
      • HshSet
      • TreeSet
      • LinkedHashSet
    • Queue  
  • Map
    • HashMap
    • ConcurrentHashMap
    • HashTable
    • TreeMap
    • LinkedHashMap

1. Colection

1.1 List

1.1.1 ArrayList

  • 底層由陣列實現;
  • 插入代價大:O(n);
  • 適合進行查詢操作:O(1);
  • 執行緒不安全;
  • 例項化ArrayLisr時不初始化容量,第一次呼叫add()方法時進行長度初始化,初始長度:10;
  • 擴容:容量不足時擴容,使用向右移位,則為當前容量的1.5倍;
    int
    newCapacity = oldCapacity + (oldCapacity >> 1);

1.1.2 Vector

  • 底層由陣列實現;
  • 支援執行緒同步(synchronized);
  • 效率低於ArrayList(由於支援執行緒同步)
  • 初始化時則初始化容量:10;
  • 擴容:每次擴容為原來兩倍;
    int newCapacity = oldCapacity * 2;

1.1.2 LinkedList

  • 連結串列結構
  • 插入、刪除操作速度快O(n);

1.2 Set

1.2.1 HashSet

  • 不按照存入順序儲存 
  • hashcode相等 && equals返回true
    時使用連結串列向下順延;

1.2.2 TreeSet

1.2.3 LinkedHshSet

2 Map

2.1 HashMap

2.1.1 java 7

  • 非執行緒安全
  • 底層實現:陣列+連結串列
  • 重要引數:
    • Capacity:陣列容量,保持2n,初始值為16,擴容後為當前容量2倍;(為什麼為2n:為了使雜湊更均勻,極端情況下查詢效率將由O(1)下降為O(n))
    • LoadFactor:負載因子,預設為0.75;
    • Threadhold:擴容閾值,Threadhold =Capacity *LoadFactor,當儲存量超過擴容閾值時,Hashmap將進行擴容。

2.1.2 java 8

  • 非執行緒安全
  • 底層實現:陣列+連結串列+紅黑樹
  • 引入紅黑樹,查詢效率由O(n)下降為O(logn)
  • HashMap之put方法流程[1] : 

1、put(key, value)中直接呼叫了內部的putVal方法,並且先對key進行了hash操作;

2、putVal方法中,先檢查HashMap資料結構中的索引陣列表是否位空,如果是的話則進行一次resize操作;

3、以HashMap索引陣列表的長度減一與key的hash值進行與運算,得出在陣列中的索引,如果索引指定的位置值為空,則新建一個k-v的新節點;

4、如果不滿足的3的條件,則說明索引指定的陣列位置的已經存在內容,這個時候稱之碰撞出現

5、在上面判斷流程走完之後,計算HashMap全域性的modCount值,以便對外部併發的迭代操作提供修改的Fail-fast判斷提供依據,於此同時增加map中的記錄數,並判斷記錄數是否觸及容量擴充的閾值,觸及則進行一輪resize操作;

6、在步驟4中出現碰撞情況時,從步驟7開始展開新一輪邏輯判斷和處理;

7、判斷key索引到的節點(暫且稱作被碰撞節點)的hash、key是否和當前待插入節點(新節點)的一致,如果是一致的話,則先儲存記錄下該節點;如果新舊節點的內容不一致時,則再看被碰撞節點是否是樹(TreeNode)型別,如果是樹型別的話,則按照樹的操作去追加新節點內容;如果被碰撞節點不是樹型別,則說明當前發生的碰撞在連結串列中(此時連結串列尚未轉為紅黑樹),此時進入一輪迴圈處理邏輯中;

8、迴圈中,先判斷被碰撞節點的後繼節點是否為空,為空則將新節點作為後繼節點,作為後繼節點之後並判斷當前連結串列長度是否超過最大允許連結串列長度8,如果大於的話,需要進行一輪是否轉樹的操作;如果在一開始後繼節點不為空,則先判斷後繼節點是否與新節點相同,相同的話就記錄並跳出迴圈;如果兩個條件判斷都滿足則繼續迴圈,直至進入某一個條件判斷然後跳出迴圈;

9、步驟8中轉樹的操作treeifyBin,如果map的索引表為空或者當前索引表長度還小於64(最大轉紅黑樹的索引陣列表長度),那麼進行resize操作就行了;否則,如果被碰撞節點不為空,那麼就順著被碰撞節點這條樹往後新增該新節點;

10、最後,回到那個被記住的被碰撞節點,如果它不為空,預設情況下,新節點的值將會替換被碰撞節點的值,同時返回被碰撞節點的值(V)。

1、在jdk1.7中,在多執行緒環境下,擴容時會造成環形鏈或資料丟失;

2、在jdk1.8中,在多執行緒環境下,會發生資料覆蓋的情況。

  • 樹化與鏈化
    • 連結串列內元素 ≥ 8
      • 陣列長度<64:擴容陣列;
      • 陣列長度 ≥ 64:連結串列樹化;
    • 連結串列內元素<6
      • 紅黑樹鏈化;

2.2 ConcurrentHashMap

2.2.1 java 7

  • 底層實現:Segment + ReentrantLock + 分段鎖
  • 並行度:16,最多支援16個執行緒併發寫(並行數由Segment數量決定)

2.2.2 java 8

  • 底層實現: 陣列 + CAS + Synchronized
  • ConcurrentHashMap之put方法

1、計算hashcode;

2、table為空,則執行初始化,預設長度16;(基於CAS)

3、否則,計算hashcode在陣列中對應的下標,並獲取下標對應的頭結點;

4、頭結點為空,新增頭結點;(基於CAS)

5、頭結點不為空,但頭結點值為 MOVED ,表示正在擴容,幫助其擴容執行緒擴容(基於CAS)

6、否則,頭結點不為空,且未處於擴容狀態,嘗試新增新結點(基於Synchronized)

7、判斷當前桶內節點數量是否大於閾值,大於則擴容 (基於CAS)

  • 連結串列內元素 ≥ 8
    • 陣列長度<64:擴容陣列;
    • 陣列長度 ≥ 64:連結串列樹化;
  • 連結串列內元素<6
    • 紅黑樹鏈化;

參考:

[1]HashMap之put方法流程解讀

[2]HashMap執行緒不安全的體現