JAVA_集合

小猴子_X發表於2022-02-04

一.體系

  • Collection:單列
    • list:有序可重複,可以放多個Null
      • Arraylist ;Linkedlist ;Vector
    • Set:無序不可重複,只能放一個Null
      • HashSet ;LinkedHashSet ;TreeSet
    • Queue:
      • Deque:雙端佇列 ;BlockingQueue:阻塞佇列 ;AbstractQueue:非阻塞佇列
  • Map:雙列,k-v鍵值對
    • HashMap
      • linkedHashMap
    • TreeMap
    • HashTable
      • Properties     
二.ArrayList、LinkedList、Vector三者的異同(使用場景)?    
  • 同:儲存有序可重複的資料 就像陣列一樣
  • 異:
    • ArrayList
      • 底層預設建立長度為10的陣列:new Object[10];(陣列就需要連續的記憶體空間)
      • 空間不夠自動擴容,擴充套件為原來 容量*1.5,同時將原有的元素複製到新的陣列中
      • jdk1.7:類似餓漢式,new Arraylist()就直接你建立一個陣列
      • jdk1.8:類似懶漢式,new Arraylist()還不建立陣列,只有使用add()才建立。延遲陣列的建立,節省記憶體
      • 陣列結構適合遍歷和查詢,不適合插入/刪除(但可以優化)
      • 執行緒不安全(效率高),可以使用Collections工具類變為執行緒安全,或使用juc
    • Vector
      • 底層預設建立長度為10的陣列:new Object[10];
      • 空間不夠自動擴容,擴充套件為原來 容量*2,同時將原有的元素複製到新的陣列中
      • 執行緒安全(所有方法synchronized修飾),但是效率太低,很少使用
    • LinkedList
      • 底層建立一個雙向連結串列 (連結串列不需要連續的記憶體空間)
      • 定義了一個Node內部類,裡面有prev,element,next屬性
      • 連結串列結構適合插入和刪除,遍歷和查詢比較慢
      • 連結串列當然不需要擴容
      • 只能使用iterator遍歷,不能使用增強for遍歷,因為需要get每一個值,遍歷所有的元素,效率極低

ArrayList和LinkedList效能對比?  建議使用ArrayList

  • LinkedList底層維護一個內部類Node,每次新增新的元素建立一個Node物件,耗費資源。 而且使用不方便(遍歷時)
  • 對於ArrayList不適合插入/刪除的特性,可以進行優化。 採用尾插法並指定初始容量可以極大的提升效能,甚至超過LinkedList。
  • ArrayList 的空間浪費主要體現在在list列表的結尾預留一定的容量空間; LinkedList 的空間花費則體現在它的每一個元素都需要消耗儲存指標節點物件的空間。

如何實現ArrayList和Array的轉換?

  • Arrays.asList(str); //轉變為list
  • list.toArray;     //轉變為array    
三.阻塞佇列

這裡只說明api的使用

 

四.HashMap

前提知識:HashCode和equals,提前說明這兩個東西,有助於理解HashMap

hashCode()相同,equals()也一定為true嗎?
不是,這兩個是配合使用的。

HashCode()是Object提供的一個native的方法,用來獲取雜湊碼
記憶體中維護一個很大的雜湊表,每一個物件儲存到記憶體的時候,都在這個表中進行記錄
這個表就相當於一個"記錄表",記錄著每個物件的地址。

什麼是雜湊表?
雜湊表本質是一個升級版的陣列,每一個物件都有一個關鍵字(k-v中的k),根據內部的"雜湊演算法"得出一個"雜湊碼"。
這個雜湊碼就相當於陣列的索引(雜湊表沒有0,1那樣的索引。雜湊碼就是索引),可以直接通過雜湊碼找到一個物件。
這樣做就是為了提高執行的效率,快速定位物件。因為雜湊表的初衷就是升級陣列,陣列已經很合適查詢了,
但是雜湊表"更塊"。雜湊表就是一種資料結構
(雜湊表其實就是對陣列的索引進行優化,"讓索引和關鍵字(傳入的物件)產生關係,從而快速找到物件的位置")

注意點:
    相同的物件一定產生相同的雜湊碼
    不同的物件也可能產生相同的雜湊碼 (產生雜湊衝突,有對應的解決方案)   
    //上面這兩條主要是因為雜湊演算法導致的,正因為這樣,才需要用到equals
    equals()被覆蓋,hashCode()也必須被覆蓋
    
為什麼要有HashCode?(為什麼搞一個"記錄表")
以HashSet如何檢查重複來說明為什麼要有HashCode:
物件加入HashSet時,HashSet會計算物件的雜湊碼,從雜湊表中檢查是否索引的位置上有值(物件),
沒有:就認為物件不重複,允許新增;
有值:就會呼叫equals()來判斷兩個物件是否相等:
        相等:不允許新增
        不相等:說明雜湊衝突了,採用對應的解決方案放到其他的位置上,允許新增
這樣做主要為了避免多次equals比較,提高效率。

HashSet底層就是建立一個HashMap,所以直接對hashMap進行解釋

底層實現:

  • jdk7:陣列+連結串列
    • new HashMap();//建立一個長度為16的陣列
  • jdk8:陣列+連結串列+紅黑樹 (改為紅黑樹為了加快查詢的速度)
    • new HashMap();//類似於懶漢式,還沒有建立陣列,當呼叫put()時建立長度為16的陣列
    • 只有當 連結串列高度>8且陣列長度>64,就把連結串列改為紅黑樹。陣列長度<6就將紅黑樹轉回連結串列

put新增過程(如何保證不重複)

  • 新增的過程和上面hashSet使用雜湊表新增的過程一樣,只是雜湊衝突問題採用七上八下
    • 注意:發現hashCode相同,equals相同,不是不允許新增,而是覆蓋之前的元素
  • 七上八下:遇到雜湊衝突時
    • jdk7是把新元素放在陣列上,舊元素放在連結串列上,指向舊元素
    • jdk8是把新元素放在連結串列上,舊元素指向新元素

擴容機制

  • 陣列超過臨界值(臨界值0.75) 擴充套件為原來的2倍, 將舊的元素複製到新的陣列中,重新計算hash,按照列表/紅黑樹的方式排序起來         

原始碼中重要的常量

  • DEFAULT_INITIAL_CAPACITY:預設陣列容量 16
  • MAXIMUM_CAPACITY:最大的容量 2^30
  • DEFAULT_LOAD_FACTOR:預設的載入因子 0.75(一個經過科學計算的數)   臨界值=容量*0.75  比如16*0.75=12 容量達到12時,考慮擴容
  • TREEIFY_THRESHOLD:連結串列轉化為紅黑樹的 連結串列最低高度 8
  • MIN_TREEIFY_CAPACITY: 連結串列轉化為紅黑樹的 陣列最小長度 64
  • UNTREEIFY_THRESHOLD:紅黑樹轉回連結串列的 陣列長度 6  

開發中你是怎麼使用hashMap?

  • 根據實際業務指定hashMap的長度,因為這樣可以避免多次擴容,提高效能
  • HashMap<String,Object> map = new HashMap<>(長度)
    • 注意:new HashMap<>(7);//這種我們自定義長度的hashmap,在建立陣列的時候,長度經過tableSizeFor(initialCapacity)方法變為大於指定長度的最低二次冪數
    • 比如1就變為2;7就變為8;11就變為16 等,所以上述是建立了一個長度為8的陣列     

Hashmap為什麼不安全?

  • jdk 1.7 hashmap底層使用陣列 + 連結串列,當擴容時會呼叫transfer函式 ,在對table進行擴 容,需要將原來的資料複製到newtable中,採用頭插法,會將連結串列反轉,這個過程可能會導致死迴圈資料丟失,也有可能造成資料覆蓋
  • jdk 1.8中 hashmap底層使用陣列 + 連結串列 + 紅黑樹,採用尾插法,優化了死迴圈和資料丟失的 問題,但是還是會有資料覆蓋的問題   

HashMap和HashTable的區別?

  • 底層不同
    • HashMap:初始化16,擴容2倍
    • HashTable:初始化11,擴容2倍+1
  • HashMap執行緒不安全,HashTable執行緒安全(效率低)。 (即使需要執行緒安全也不用HashTable,而是使用concurrentHashMap,後面解釋)
  • HashMap可以儲存null的k-v;HashTable不能儲存null的k-v

 

 

寄語:任何你的不足,在你成功的那刻,都會被人說為特色。所以,堅持做你自己,而不是在路上被別人修改的面目全非

相關文章