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 重要特色
- 本質實現:Object類型的動態的數組。
- 線程安全:非同步的。
- 列表長度:ArrayList中元素個數用size記錄。
- 擴展容量:初始化容量 = 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 對象的容量,這樣就避免添加元素滿了時擴容、挨個複製後移等消耗。
- 3種構造方法:1)構造一個默認初始容量爲10的空列表; 2) 構造一個指定初始容量的空列表; 3) 構造一個包含指定collection的元素的列表,內部是Arrays.copyOf(elementData, size, Object[].class);。
- 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()。
- 2種刪除方法:1) remove(int index) ;2)remove(Object o)
- 轉換成數組:toArray()方法內部調用Arrays.copyOf(),toArray(T[] a)方法內部若是是部分轉換用Arrays.copyOf(),所有轉換用 System.arraycopy()。
- 遍歷方法:遍歷時 get 的效率要 >= 迭代器。
- Fail-Fast機制:ArrayList 不是同步的,因此在併發訪問時,若是在迭代器迭代的同時有其餘線程修改了 ArrayList, fail-fast 的迭代器 Iterator/ListIterator 會報 ConcurrentModificationException 錯。所以咱們在併發環境下須要外部給 ArrayList 加個同步鎖,或者直接在初始化時用 Collections.synchronizedList 方法進行包裝。也可使用concurrent併發包下的CopyOnWriteArrayList類。快速失敗機制經過記錄modCount參數來實現。迭代器的快速失敗行爲應該僅用於檢測 bug。
- 序列化:ArrayList實現java.io.Serializable的方式。當寫入到輸出流時,先寫入「容量」,再依次寫入「每個元素」;當讀出輸入流時,先讀取「容量」,再依次讀取「每個元素」。那爲何ArrayList裏面的elementData爲何要用transient來修飾?不是由於ArrayList不能序列化和反序列化,是由於elementData裏面不是全部的元素都有數據,由於容量的問題,elementData裏面有一些元素是空的,這種是沒有必要序列化的。ArrayList的序列化和反序列化依賴writeObject和readObject方法來實現。能夠避免序列化空的元素。序列化size大小的元素。
- 克隆: ArrayList的本質是維護了一個Object的數組,因此克隆也是經過數組的複製實現的,屬於淺複製,調用的是Arrays.copyOf()。若是你想要修改克隆後的集合,那麼克隆前的也會被修改。那麼就須要使用深複製。經過實現對象類的clone方法。
- ArrayList的實現中大量地調用了Arrays.copyof()和System.arraycopy()方法。
- ArrayList基於數組實現,能夠經過下標索引直接查找到指定位置的元素,所以查找效率高,但每次插入或刪除元素,就要大量地移動元素,插入刪除元素的效率低。
- 在查找給定元素索引值等的方法中,源碼都將該元素的值分爲null和不爲null兩種狀況處理,ArrayList中容許元素爲null。
- indexOf和lastIndexOf 查找元素,若元素不存在,則返回-1!
- 適合讀數據多的場合。
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 重要特色
- 本質實現:底層使用一個Node數據結構,有先後兩個指針,雙向鏈表實現。
- 線程安全:非同步的。
- 列表長度:LinkedList中元素個數用size記錄。
- 列表容量:LinkedList是基於鏈表實現的,所以不存在容量不足的問題,因此這裏沒有擴容的方法。
- 內存:須要更多的內存,LinkedList 每一個節點中須要多存儲先後節點的信息,佔用空間更多些(previous element next)。
- 元素容許爲null。
- Fail-Fast機制:同ArrayList相同。
- 遍歷方法:全部指定位置的操做都是從頭開始遍歷進行的。LinkedList是基於鏈表實現的,所以插入刪除效率高,查找效率低(雖然有一個加速動做)。源碼中先將index與長度size的一半比較,若是index<size/2,就只從位置0日後遍歷到位置index處,而若是index>size/2,就只從位置size往前遍歷到位置index處。這樣能夠減小一部分沒必要要的遍歷,從而提升必定的效率(實際上效率仍是很低)。Arrays.copyOf() 方法:
- 它適合刪除,插入較多的場景。
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 重要特色
- 本質實現:可增加的對象數組。
- 線程安全:同步的,不少方法都加入了synchronized同步語句,來保證線程安全。
- 列表長度:elementCount表示實際元素的數量。(Vector 經過 capacity (容量) 和 capacityIncrement (增加數量) 來儘可能少的佔用空間)
- 列表容量:Vector初始化容量是10,擴容默認2倍。capacityIncrement 容量增加係數(向量的大小大於其容量時,容量自動增長的量),ensureCapacity(int minCapacity)調用ensureCapacityHelper(int minCapacity)若是此向量的當前容量小於
minCapacity
,則經過將其內部數據數組(保存在字段 elementData
中)替換爲一個較大的數組來增長其容量。新數據數組的大小將爲原來的大小加上 capacityIncrement
,若是容量的增量小於等於零,則每次須要增大容量時,向量的容量將增大一倍(編程原來的兩倍),不過,若是此大小仍然小於 minCapacity
,則新容量將爲 minCapacity
。
- Fail-Fast機制:Vector 的 iterator 和 listIterator 方法所返回的迭代器是快速失敗的:若是在迭代器建立後的任意時間從結構上修改了向量(經過迭代器自身的 remove 或 add 方法以外的任何其餘方式),則迭代器將拋出 ConcurrentModificationException。所以,面對併發的修改,迭代器很快就徹底失敗,而不是冒着在未來不肯定的時間任意發生不肯定行爲的風險。
- Vector 的 elements 方法返回的 Enumeration 不是 快速失敗的。
- Vector 主要用在事先不知道數組的大小,或者只是須要一個能夠改變大小的數組的狀況。
- 最好在插入大量元素前增長 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 重要特色
- Stack是棧。它的特性是:先進後出(FILO, First In Last Out)。
- Stack實際上也是經過數組去實現的。實際調用的實現方法都是Vector中的方法!
- push時(即,將元素推入棧中),是經過將元素追加的數組的末尾中。
- peek時(即,取出棧頂元素,不執行刪除),是返回數組末尾的元素。
- pop時(即,取出棧頂元素,並將該元素從棧中刪除),是取出數組末尾的元素,而後將該元素從數組中刪除。
- 棧最大的長度取決於vector裏面數組能有多長。這裏vector裏面最大能取到Integer.MAX_VALUE。
5.CopyOnWriteArrayList(JUC)
1. CopyOnWriteArrayList 結構圖
CopyOnWrite容器即寫時複製的容器。通俗的理解是當咱們往一個容器添加元素的時候,不直接往當前容器添加,而是先將當前容器進行Copy,複製出一個新的容器,而後新的容器裏添加元素,添加完元素以後,再將原容器的引用指向新的容器。這樣作的好處是咱們能夠對CopyOnWrite容器進行併發的讀,而不須要加鎖,由於當前容器不會添加任何元素。因此CopyOnWrite容器也是一種讀寫分離的思想,讀和寫不一樣的容器。添加的時候是須要加鎖的.
2. CopyOnWriteArrayList重要特色
- 增刪改都須要得到鎖,而且鎖只有一把,而讀操做不須要得到鎖,支持併發。(而Vector讀也須要加鎖,性能差)
- 讀的時候不須要加鎖,若是讀的時候有多個線程正在向CopyOnWriteArrayList添加數據,讀仍是會讀到舊的數據,由於寫的時候不會鎖住舊的CopyOnWriteArrayList。
- 應用:CopyOnWrite併發容器用於讀多寫少的併發場景。好比白名單,黑名單,商品類目的訪問和更新場景。
- 缺點:即內存佔用問題(寫時複製機制->GC->應用響應時間長)和數據一致性問題(只能保證數據的最終一致性,不能保證數據的實時一致性)。
- CopyOnWriteArrayList 是一個線程安全的 ArrayList,經過內部的 volatile 數組和顯式鎖 ReentrantLock 來實現線程安全。
-
CopyOnWriteArrayList 實現很是簡單。內部使用了一個 volatile 數組(array)來存儲數據,保證了多線程環境下的可見性。在更新數據時,都會新建一個數組,並將更新後的數據拷貝到新建的數組中,最後再將該數組賦值給 array。正因爲這個緣由,涉及到數據更新的操做效率很低。
抄錄網址
- https://blog.csdn.net/u010648555/column/info/14681(Java集合系列專欄)
- https://blog.csdn.net/ns_code/article/category/2362915(Java集合源碼剖析)
- http://www.cnblogs.com/skywang12345/p/3323085.html( Java 集合系列)
- http://ifeve.com/talk-concurrency/(聊聊併發系列)
- Java List集合深刻學習
- java集合系列——List集合之ArrayList介紹(二)
- 深刻Java集合學習系列:ArrayList的實現原理
- java集合入門和深刻學習,看這篇就差很少了
- ArrayList 源碼分析 -- 擴容問題及序列化問題
- 【Java集合源碼剖析】ArrayList源碼剖析
- ArrayList的elementData爲何要用transient修飾
- java ArrayList的序列化分析
- 何巧妙的使用ArrayList的Clone方法
- Java 集合深刻理解(11):LinkedList
- java集合系列——List集合之LinkedList介紹(三)
- 【Java集合源碼剖析】LinkedList源碼剖析
- LinkedList API
- 聊聊併發-Java中的Copy-On-Write容器