Java 集合系列之二:List基本操做 java ArrayList的序列化分析 何巧妙的使用ArrayList的Clone方法

1. Java List

1. Java List重要觀點

  • Java List接口是Java Collections Framework的成員。
  • List容許您添加劇復元素。
  • List容許您擁有'null'元素。
  • List接口在Java 8中有許多默認方法,例如replaceAll,sort和spliterator。
  • 列表索引從0開始,就像數組同樣。
  • List支持泛型(類型的參數化),咱們應儘量使用它。將Generics與List一塊兒使用將在運行時避免ClassCastException。

2. Java列表類圖

Java List接口擴展了Collection接口。Collection接口 externs Iterable接口。html

一些最經常使用的List實現類是ArrayList,LinkedList,Vector,Stack,CopyOnWriteArrayList。java

AbstractList提供了List接口的骨幹實現,以減小實現List的工做量。編程

   

3. Java List方法

  • int size():獲取列表中元素的數量。
  • boolean isEmpty():檢查列表是否爲空。
  • boolean contains(Object o):若是此列表包含指定的元素,則返回true。
  • Iterator <E> iterator():以適當的順序返回此列表中元素的迭代器。
  • Object [] toArray():以適當的順序返回包含此列表中全部元素的數組
  • boolean add(E e):將指定的元素追加到此列表的末尾。
  • boolean remove(Object o):今後列表中刪除指定元素的第一個匹配項。
  • boolean retainAll(Collection <?> c):僅保留此列表中包含在指定集合中的元素。
  • void clear():從列表中刪除全部元素。
  • E get(int index):返回列表中指定位置的元素。
  • E set(int index,E element):用指定的元素替換列表中指定位置的元素。
  • ListIterator <E> listIterator():返回列表中元素的列表迭代器。
  • List <E> subList(int fromIndex,int toIndex):返回指定fromIndex(包含)和toIndex(不包括)之間的此列表部分的視圖。返回的列表由此列表支持,所以返回列表中的非結構更改將反映在此列表中,反之亦然。

在Java 8中添加到List的一些默認方法是;api

  • default void replaceAll(UnaryOperator <E>運算符):將此列表的每一個元素替換爲將運算符應用於該元素的結果。
  • default void sort(Comparator <super E> c):根據指定的Comparator引起的順序對此列表進行排序。
  • default Spliterator <E> spliterator():在此列表中的元素上建立Spliterator。

2. ArrayList

1. ArrayList 結構圖

ArrayList基於數組實現,是一個動態的數組鏈表。可是它和Java中的數組又不同,它的容量能夠自動增加,相似於C語言中動態申請內存,動態增加內存!
ArrayList繼承了AbstractList,實現了RandomAccess、Cloneable和Serializable接口!數組

  • 實現了RandomAccess接口,提供了隨機訪問功能,實際上就是經過下標序號進行快速訪問,所以查找效率高。
  • 實現了Cloneable接口,即覆蓋了函數clone(),實現淺拷貝。
  • 實現了Serializable接口,支持序列化,也就意味了ArrayList可以經過序列化傳輸。

2. ArrayList 重要特色

  1. 本質實現:Object類型的動態的數組
  2. 線程安全:非同步的。
  3. 列表長度:ArrayList中元素個數用size記錄。
  4. 擴展容量:初始化容量 = 10 ,最大容量不會超過 MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8!【Integer.MAX_VALUE = 0x7fffffff,換算成二進制: 2^31 - 1,十進制就是 :2147483647,二十一億多。一些虛擬器須要在數組前加個 頭標籤,因此減去 8 。 當想要分配比 MAX_ARRAY_SIZE 大的個數就會報 OutOfMemoryError。】當ArrayList容量不足以容納所有元素時,ArrayList會從新設置容量:JDK1.6 int newCapacity = (oldCapacity * 3) /2 + 1;   JDK1.8 int newCapacity = oldCapacity + (oldCapacity >> 1); 當容量不夠時,調用ensureCapacity(int minCapacity)方法調整列表容量,每次增長元素,都要將原來的元素拷貝到一個新的數組中,使用Arrays.copyOf(elementData, newCapacity)拷貝 ,很是之耗時,也所以建議在事先能肯定元素數量的狀況下,才使用ArrayList,不然建議使用LinkedList。咱們能夠主動調用 ensureCapcity 來增長 ArrayList 對象的容量,這樣就避免添加元素滿了時擴容、挨個複製後移等消耗。
  5. 3種構造方法:1)構造一個默認初始容量爲10的空列表; 2) 構造一個指定初始容量的空列表; 3) 構造一個包含指定collection的元素的列表,內部是Arrays.copyOf(elementData, size, Object[].class);。
  6. 5種存儲方法:1)set(int index, E element)、2)add(E e)、3)add(int index, E element)、4)addAll(Collection<? extends E> c)、5)addAll(int index, Collection<? extends E> c) 其中3,4,5調用了 System.arraycopy()
  7. 2種刪除方法:1) remove(int index) ;2)remove(Object o) 
  8. 轉換成數組:toArray()方法內部調用Arrays.copyOf(),toArray(T[] a)方法內部若是是部分轉換用Arrays.copyOf(),所有轉換用 System.arraycopy()
  9. 遍歷方法:遍歷時 get 的效率要 >= 迭代器。
  10. Fail-Fast機制:ArrayList 不是同步的,因此在併發訪問時,若是在迭代器迭代的同時有其餘線程修改了 ArrayList, fail-fast 的迭代器 Iterator/ListIterator 會報 ConcurrentModificationException 錯。所以咱們在併發環境下須要外部給 ArrayList 加個同步鎖,或者直接在初始化時用 Collections.synchronizedList 方法進行包裝。也可使用concurrent併發包下的CopyOnWriteArrayList類。快速失敗機制經過記錄modCount參數來實現。迭代器的快速失敗行爲應該僅用於檢測 bug。
  11. 序列化:ArrayList實現java.io.Serializable的方式。當寫入到輸出流時,先寫入「容量」,再依次寫入「每個元素」;當讀出輸入流時,先讀取「容量」,再依次讀取「每個元素」。那爲何ArrayList裏面的elementData爲何要用transient來修飾?不是由於ArrayList不能序列化和反序列化,是由於elementData裏面不是全部的元素都有數據,由於容量的問題,elementData裏面有一些元素是空的,這種是沒有必要序列化的。ArrayList的序列化和反序列化依賴writeObject和readObject方法來實現。能夠避免序列化空的元素。序列化size大小的元素。
  12. 克隆: ArrayList的本質是維護了一個Object的數組,因此克隆也是經過數組的複製實現的,屬於淺複製,調用的是Arrays.copyOf()。若是你想要修改克隆後的集合,那麼克隆前的也會被修改。那麼就須要使用深複製。經過實現對象類的clone方法。
  13. ArrayList的實現中大量地調用了Arrays.copyof()和System.arraycopy()方法。
  14. ArrayList基於數組實現,能夠經過下標索引直接查找到指定位置的元素,所以查找效率高,但每次插入或刪除元素,就要大量地移動元素,插入刪除元素的效率低。
  15. 在查找給定元素索引值等的方法中,源碼都將該元素的值分爲null和不爲null兩種狀況處理,ArrayList中容許元素爲null。
  16. indexOf和lastIndexOf 查找元素,若元素不存在,則返回-1!
  17. 適合讀數據多的場合。

2. LinkedList

1. LinkedList 結構圖

LinkedList是基於鏈表實現的,從源碼能夠看出是一個雙向鏈表。除了當作鏈表使用外,它也能夠被看成堆棧、隊列或雙端隊列進行操做安全

LinkedList不是線程安全的,繼承AbstractSequentialList實現List、Deque、Cloneable、Serializable。數據結構

  • LinkedList繼承AbstractSequentialList,AbstractSequentialList 實現了get(int index)、set(int index, E element)、add(int index, E element) 和 remove(int index)這些函數。這些接口都是隨機訪問List的。
  • LinkedList 實現 List 接口,能對它進行隊列操做。
  • LinkedList 實現 Deque 接口,即能將LinkedList看成雙端隊列使用。
  • LinkedList 實現了Cloneable接口,即覆蓋了函數clone(),能克隆。
  • LinkedList 實現java.io.Serializable接口,這意味着LinkedList支持序列化,能經過序列化去傳輸。

2. LinkedList 重要特色

  1. 本質實現:底層使用一個Node數據結構,有先後兩個指針,雙向鏈表實現。
  2. 線程安全:非同步的。
  3. 列表長度:LinkedList中元素個數用size記錄。
  4. 列表容量:LinkedList是基於鏈表實現的,所以不存在容量不足的問題,因此這裏沒有擴容的方法。
  5. 內存:須要更多的內存,LinkedList 每一個節點中須要多存儲先後節點的信息,佔用空間更多些(previous  element next)。
  6. 元素容許爲null。
  7. Fail-Fast機制:同ArrayList相同。
  8. 遍歷方法:全部指定位置的操做都是從頭開始遍歷進行的。LinkedList是基於鏈表實現的,所以插入刪除效率高,查找效率低(雖然有一個加速動做)。源碼中先將index與長度size的一半比較,若是index<size/2,就只從位置0日後遍歷到位置index處,而若是index>size/2,就只從位置size往前遍歷到位置index處。這樣能夠減小一部分沒必要要的遍歷,從而提升必定的效率(實際上效率仍是很低)。Arrays.copyOf() 方法:
  9. 它適合刪除,插入較多的場景。

3. Vector

1. Vector 結構圖

Vector 類能夠實現可增加的對象數組。與數組同樣,它包含可使用整數索引進行訪問的組件。可是,Vector 的大小能夠根據須要增大或縮小,以適應建立 Vector 後進行添加或移除項的操做。Vector 是同步的,可用於多線程。多線程

  • Vector 繼承了AbstractList,實現了List;因此,它是一個隊列,支持相關的添加、刪除、修改、遍歷等功能。
  • Vector實現了RandmoAccess接口,即提供了隨機訪問功能。RandmoAccess是java中用來被List實現,爲List提供快速訪問功能的。在Vector中,咱們便可以經過元素的序號快速獲取元素對象;這就是快速隨機訪問。
  • Vector 實現了Cloneable接口,即實現clone()函數。它能被克隆。
  • Vector 實現Serializable接口,支持序列化。

2. Vector 重要特色

  1. 本質實現:可增加的對象數組。
  2. 線程安全:同步的,不少方法都加入了synchronized同步語句,來保證線程安全。
  3. 列表長度:elementCount表示實際元素的數量。(Vector 經過 capacity (容量) 和 capacityIncrement (增加數量) 來儘可能少的佔用空間)
  4. 列表容量:Vector初始化容量是10,擴容默認2倍。capacityIncrement 容量增加係數(向量的大小大於其容量時,容量自動增長的量),ensureCapacity(int minCapacity)調用ensureCapacityHelper(int minCapacity)若是此向量的當前容量小於 minCapacity,則經過將其內部數據數組(保存在字段 elementData 中)替換爲一個較大的數組來增長其容量。新數據數組的大小將爲原來的大小加上 capacityIncrement,若是容量的增量小於等於零,則每次須要增大容量時,向量的容量將增大一倍(編程原來的兩倍),不過,若是此大小仍然小於 minCapacity,則新容量將爲 minCapacity
  5. Fail-Fast機制:Vector 的 iterator 和 listIterator 方法所返回的迭代器是快速失敗的:若是在迭代器建立後的任意時間從結構上修改了向量(經過迭代器自身的 remove 或 add 方法以外的任何其餘方式),則迭代器將拋出 ConcurrentModificationException。所以,面對併發的修改,迭代器很快就徹底失敗,而不是冒着在未來不肯定的時間任意發生不肯定行爲的風險。
  6. Vector 的 elements 方法返回的 Enumeration 不是 快速失敗的。
  7. Vector 主要用在事先不知道數組的大小,或者只是須要一個能夠改變大小的數組的狀況。
  8. 最好在插入大量元素前增長 vector 容量,那樣能夠減小從新申請內存的次數。

3. Array vs Vector

共同點:併發

  • 都是基於數組
  • 都支持隨機訪問
  • 默認容量都是 10
  • 都有擴容機制

區別:dom

  • Vector 出生的比較早,JDK 1.0 就出生了,ArrayList JDK 1.2 纔出來
  • Vector 比 ArrayList 多一種迭代器 Enumeration
  • Vector 是線程安全的,ArrayList 不是
  • Vector 默認擴容 2 倍,ArrayList 是 1.5

若是沒有線程安全的需求,通常推薦使用 ArrayList,而不是 Vector,由於每次都要獲取鎖,效率過低。

4. Stack

1. Stack 結構圖

Stack 類表示後進先出(LIFO)的對象堆棧。它經過五個操做對類 Vector 進行了擴展 ,容許將向量視爲堆棧。它提供了一般的 push 和 pop 操做,以及取堆棧頂點的 peek 方法、測試堆棧是否爲空的 empty 方法、在堆棧中查找項並肯定到堆棧頂距離的 search 方法。
由於它繼承自Vector,那麼它的實現原理是以數組實現堆棧的。若是要以鏈表方式實現堆棧可使用LinkedList!(由於)

2. Stack 重要特色

  1. Stack是棧。它的特性是:先進後出(FILO, First In Last Out)。
  2. Stack實際上也是經過數組去實現的。實際調用的實現方法都是Vector中的方法!
  3. push時(即,將元素推入棧中),是經過將元素追加的數組的末尾中。
  4. peek時(即,取出棧頂元素,不執行刪除),是返回數組末尾的元素。
  5. pop時(即,取出棧頂元素,並將該元素從棧中刪除),是取出數組末尾的元素,而後將該元素從數組中刪除。
  6. 棧最大的長度取決於vector裏面數組能有多長。這裏vector裏面最大能取到Integer.MAX_VALUE。 

5.CopyOnWriteArrayList(JUC) 

1. CopyOnWriteArrayList 結構圖

CopyOnWrite容器即寫時複製的容器。通俗的理解是當咱們往一個容器添加元素的時候,不直接往當前容器添加,而是先將當前容器進行Copy,複製出一個新的容器,而後新的容器裏添加元素,添加完元素以後,再將原容器的引用指向新的容器。這樣作的好處是咱們能夠對CopyOnWrite容器進行併發的讀,而不須要加鎖,由於當前容器不會添加任何元素。因此CopyOnWrite容器也是一種讀寫分離的思想,讀和寫不一樣的容器。添加的時候是須要加鎖的.

2. CopyOnWriteArrayList重要特色

  1. 增刪改都須要得到鎖,而且鎖只有一把,而讀操做不須要得到鎖,支持併發。(而Vector讀也須要加鎖,性能差)
  2. 讀的時候不須要加鎖,若是讀的時候有多個線程正在向CopyOnWriteArrayList添加數據,讀仍是會讀到舊的數據,由於寫的時候不會鎖住舊的CopyOnWriteArrayList。
  3. 應用:CopyOnWrite併發容器用於讀多寫少的併發場景。好比白名單,黑名單,商品類目的訪問和更新場景。
  4. 缺點:即內存佔用問題(寫時複製機制->GC->應用響應時間長)和數據一致性問題(只能保證數據的最終一致性,不能保證數據的實時一致性)。
  5. CopyOnWriteArrayList 是一個線程安全的 ArrayList,經過內部的 volatile 數組和顯式鎖 ReentrantLock 來實現線程安全。
  6. CopyOnWriteArrayList 實現很是簡單。內部使用了一個 volatile 數組(array)來存儲數據,保證了多線程環境下的可見性。在更新數據時,都會新建一個數組,並將更新後的數據拷貝到新建的數組中,最後再將該數組賦值給 array。正因爲這個緣由,涉及到數據更新的操做效率很低。

抄錄網址

  1. https://blog.csdn.net/u010648555/column/info/14681(Java集合系列專欄)
  2. https://blog.csdn.net/ns_code/article/category/2362915(Java集合源碼剖析)
  3. http://www.cnblogs.com/skywang12345/p/3323085.html( Java 集合系列)
  4. http://ifeve.com/talk-concurrency/(聊聊併發系列)
  5. Java List集合深刻學習
  6. java集合系列——List集合之ArrayList介紹(二)
  7. 深刻Java集合學習系列:ArrayList的實現原理
  8. java集合入門和深刻學習,看這篇就差很少了
  9. ArrayList 源碼分析 -- 擴容問題及序列化問題
  10. 【Java集合源碼剖析】ArrayList源碼剖析
  11. ArrayList的elementData爲何要用transient修飾
  12. java ArrayList的序列化分析
  13. 何巧妙的使用ArrayList的Clone方法
  14. Java 集合深刻理解(11):LinkedList
  15. java集合系列——List集合之LinkedList介紹(三)
  16. 【Java集合源碼剖析】LinkedList源碼剖析
  17. LinkedList API
  18. 聊聊併發-Java中的Copy-On-Write容器
相關文章
相關標籤/搜索