1. 程式人生 > >Effective Java 第二版 中文版 筆記(二十五)列表優於陣列

Effective Java 第二版 中文版 筆記(二十五)列表優於陣列

不知道說的什麼,總之是一般情況下都是用List。

陣列、List和ArrayList的區別

        陣列在記憶體中是連續儲存的,所以它的索引速度是非常的快,而且賦值與修改元素也很簡單。

       但是陣列也存在一些不足的地方。比如在陣列的兩個資料間插入資料也是很麻煩的,還有我們在宣告陣列的時候,必須同時指明陣列的長度,陣列的長度過長,會造成記憶體浪費,陣列和長度過短,會造成資料溢位的錯誤。

     ArrayList是.Net Framework提供的用於資料儲存和檢索的專用類,它是名稱空間System.Collections下的一部分。它的大小是按照其中儲存的資料來動態擴充與收縮的。所以,我們在宣告ArrayList物件時並不需要指定它的長度。ArrayList繼承了IList介面,所以它可以很方便的進行資料的新增,插入和移除.。

在list中,我們不僅插入了字串"abc",而且又插入了數字123。這樣在ArrayList中插入不同型別的資料是允許的。因為ArrayList會把所有插入其中的資料都當作為object型別來處理。這樣,在我們使用ArrayList中的資料來處理問題的時候,很可能會報型別不匹配的錯誤,也就是說ArrayList不是型別安全的。既使我們保證在插入資料的時候都很小心,都有插入了同一型別的資料,但在使用的時候,我們也需要將它們轉化為對應的原型別來處理。這就存在了裝箱與拆箱的操作,會帶來很大的效能損耗。

  裝箱與拆箱的概念: 簡單的來講: 裝箱:就是將值型別的資料打包到引用型別的例項中 比如將int型別的值123賦給object物件o

  int i=123; object o=(object)i;

  拆箱:就是從引用資料中提取值型別 比如將object物件o的值賦給int型別的變數i

  object o=123; int i=(int)o;

  裝箱與拆箱的過程是很損耗效能的。

  正是因為ArrayList存在不安全型別與裝箱拆箱的缺點,所以在C#2.0後出現了泛型的概念。而List類是ArrayList類的泛型等效類。它的大部分用法都與ArrayList相似,因為List類也繼承了IList介面。最關鍵的區別在於,在宣告List集合時,我們同時需要為其宣告List集合內資料的物件型別。

ArrayList實現原理要點概括

  1. ArrayList是List介面的可變陣列非同步實現,並允許包括null在內的所有元素。
  2. 底層使用陣列實現
  3. 該集合是可變長度陣列,陣列擴容時,會將老陣列中的元素重新拷貝一份到新的陣列中,每次陣列容量增長大約是其容量的1.5倍,這種操作的代價很高。
  4. 採用了Fail-Fast機制,面對併發的修改時,迭代器很快就會完全失敗,而不是冒著在將來某個不確定時間發生任意不確定行為的風險
  5. remove方法會讓下標到陣列末尾的元素向前移動一個單位,並把最後一位的值置空,方便GC

LinkedList實現原理要點概括

  1. LinkedList是List介面的雙向連結串列非同步實現,並允許包括null在內的所有元素。
  2. 底層的資料結構是基於雙向連結串列的,該資料結構我們稱為節點
  3. 雙向連結串列節點對應的類Node的例項,Node中包含成員變數:prev,next,item。其中,prev是該節點的上一個節點,next是該節點的下一個節點,item是該節點所包含的值。
  4. 它的查詢是分兩半查詢,先判斷index是在連結串列的哪一半,然後再去對應區域查詢,這樣最多隻要遍歷連結串列的一半節點即可找到

HashMap實現原理要點概括

  1. HashMap是基於雜湊表的Map介面的非同步實現,允許使用null值和null鍵,但不保證對映的順序。
  2. 底層使用陣列實現,陣列中每一項是個單向連結串列,即陣列和連結串列的結合體;當連結串列長度大於一定閾值時,連結串列轉換為紅黑樹,這樣減少連結串列查詢時間。
  3. HashMap在底層將key-value當成一個整體進行處理,這個整體就是一個Node物件。HashMap底層採用一個Node[]陣列來儲存所有的key-value對,當需要儲存一個Node物件時,會根據key的hash演算法來決定其在陣列中的儲存位置,在根據equals方法決定其在該陣列位置上的連結串列中的儲存位置;當需要取出一個Node時,也會根據key的hash演算法找到其在陣列中的儲存位置,再根據equals方法從該位置上的連結串列中取出該Node。
  4. HashMap進行陣列擴容需要重新計算擴容後每個元素在陣列中的位置,很耗效能
  5. 採用了Fail-Fast機制,通過一個modCount值記錄修改次數,對HashMap內容的修改都將增加這個值。迭代器初始化過程中會將這個值賦給迭代器的expectedModCount,在迭代過程中,判斷modCount跟expectedModCount是否相等,如果不相等就表示已經有其他執行緒修改了Map,馬上丟擲異常

Hashtable實現原理要點概括

  1. Hashtable是基於雜湊表的Map介面的同步實現,不允許使用null值和null鍵
  2. 底層使用陣列實現,陣列中每一項是個單鏈表,即陣列和連結串列的結合體
  3. Hashtable在底層將key-value當成一個整體進行處理,這個整體就是一個Entry物件。Hashtable底層採用一個Entry[]陣列來儲存所有的key-value對,當需要儲存一個Entry物件時,會根據key的hash演算法來決定其在陣列中的儲存位置,在根據equals方法決定其在該陣列位置上的連結串列中的儲存位置;當需要取出一個Entry時,也會根據key的hash演算法找到其在陣列中的儲存位置,再根據equals方法從該位置上的連結串列中取出該Entry。
  4. synchronized是針對整張Hash表的,即每次鎖住整張表讓執行緒獨佔

ConcurrentHashMap實現原理要點概括

  1. ConcurrentHashMap允許多個修改操作併發進行,其關鍵在於使用了鎖分離技術。
  2. 它使用了多個鎖來控制對hash表的不同段進行的修改,每個段其實就是一個小的hashtable,它們有自己的鎖。只要多個併發發生在不同的段上,它們就可以併發進行。
  3. ConcurrentHashMap在底層將key-value當成一個整體進行處理,這個整體就是一個Entry物件。Hashtable底層採用一個Entry[]陣列來儲存所有的key-value對,當需要儲存一個Entry物件時,會根據key的hash演算法來決定其在陣列中的儲存位置,在根據equals方法決定其在該陣列位置上的連結串列中的儲存位置;當需要取出一個Entry時,也會根據key的hash演算法找到其在陣列中的儲存位置,再根據equals方法從該位置上的連結串列中取出該Entry。
  4. 與HashMap不同的是,ConcurrentHashMap使用多個子Hash表,也就是段(Segment)
  5. ConcurrentHashMap完全允許多個讀操作併發進行,讀操作並不需要加鎖。如果使用傳統的技術,如HashMap中的實現,如果允許可以在hash鏈的中間新增或刪除元素,讀操作不加鎖將得到不一致的資料。ConcurrentHashMap實現技術是保證HashEntry幾乎是不可變的。

HashSet實現原理要點概括

  1. HashSet由雜湊表(實際上是一個HashMap例項)支援,不保證set的迭代順序,並允許使用null元素。
  2. 基於HashMap實現,API也是對HashMap的行為進行了封裝,可參考HashMap

LinkedHashMap實現原理要點概括

  1. LinkedHashMap繼承於HashMap,底層使用雜湊表和雙向連結串列來儲存所有元素,並且它是非同步,允許使用null值和null鍵。
  2. 基本操作與父類HashMap相似,通過重寫HashMap相關方法,重新定義了陣列中儲存的元素Entry,來實現自己的連結列表特性。該Entry除了儲存當前物件的引用外,還儲存了其上一個元素before和下一個元素after的引用,從而構成了雙向連結列表。

LinkedHashSet實現原理要點概括

  1. 對於LinkedHashSet而言,它繼承與HashSet、又基於LinkedHashMap來實現的。LinkedHashSet底層使用LinkedHashMap來儲存所有元素,它繼承與HashSet,其所有的方法操作上又與HashSet相同