要想深刻的瞭解集合就必需要經過分析源碼來了解它,那如何來看源碼,要看什麼東西呢?主要從三個方面:html
一、看繼承結構java
看這個類的繼承結構,處於一個什麼位置,不須要背記,有個大概的感受就能夠,我本身感受了解了以後內心都舒服些。數組
二、看構造方法安全
很重要,通常在構造方法中會作不少事情,要跟蹤方法中的方法。dom
三、看經常使用方法函數
不必全部方法都去了解,知道經常使用、核心的方法實現便可。源碼分析
本文參考:http://www.javashuo.com/article/p-dxhxlxrw-co.html性能
1)ArrayList是能夠動態增加和縮減的索引序列,它是基於數組實現的List類。this
2)該類封裝了一個動態再分配的Object[]數組,每個類對象都有一個capacity屬性,表示它們所封裝的Object[]數組的長度,當向ArrayList中添加元素時,該屬性值會自動增長。spa
3)若是想ArrayList中添加大量元素,可以使用ensureCapacity方法一次性增長capacity,能夠減小增長重分配的次數提升性能。
4)ArrayList的用法和Vector向相似,可是Vector是一個較老的集合,具備不少缺點,不建議使用。
另外,ArrayList和Vector的區別是:ArrayList是線程不安全的,當多條線程訪問同一個ArrayList集合時,程序須要手動保證該集合的同步性,而Vector則是線程安全的。
5)繼承關係圖:
分析:
爲何要讓AbstractList先實現List<E>,而後在讓ArrayList繼承AbstractList?爲何不讓ArrayList直接實現List<E>?
這裏是一種默認的寫法,也能夠說是一種思想:讓AbstractList去實現接口中一些通用的方法,而具體的類ArrayList就繼承這個AbstractList類,拿到一些通用的方法,而後本身在實現一些本身特有的方法。
這樣一來代碼更簡潔,而且若是有多個類繼承ArrayList,就能夠直接繼承ArrayList中通用的方法,減小重複代碼。因此通常看到一個類上面還有一個抽象類,應該就是這個做用。
沒什麼可說的,看註釋便可。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable { // 版本號 private static final long serialVersionUID = 8683452581122892189L; // 缺省容量 private static final int DEFAULT_CAPACITY = 10; // 空對象數組 private static final Object[] EMPTY_ELEMENTDATA = {}; // 缺省空對象數組 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 元素數組 transient Object[] elementData; // 實際元素大小,默認爲0 private int size; // 最大數組容量 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; }
ArrayList有三個構造方法:
一、無參構造函數
DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一個空的Object[],將elementData初始化,elementData也是個Object[]類型。空的Object[]會給默認大小10,等會解釋何時賦值的。
ArrayList中儲存數據的其實就是一個數組,這個數組就是elementData,下圖中有
二、有參構造函數一
邏輯很簡單,不解釋了。
三、有參構成函數二(不經常使用)
總結:arrayList的構造方法就作一件事情,就是初始化一下儲存數據的容器,其實本質上就是一個數組,在其中就叫elementData。
添加方法共有四個,這裏只介紹經常使用的兩種。
1)boolean add(E);//默認直接在末尾添加元素
分析:
ensureCapacityInternal方法爲肯定容量方法。
在添加元素以前須要肯定數組是否能放的下,size是數組中數據的個數,由於要添加一個元素,因此size+1。
ensureCapacityInternal方法中分兩步:
a、首先肯定最小容量:判斷elementData ==DEFAULTCAPACITY_EMPTY_ELEMENTDATA,即判斷初始化的elementData是否是空的數組。而後找出默認容量和參數容量中大的。
b、調用ensureExplicitCapacity方法,該方法纔是真的判斷容量是否夠用的方法,若是不過用則擴容
在ensureExplicitCapacity方法中,若是須要的容量大於elementData的容量,則調用grow方法進行擴容,grow方法是真正的擴容方法。
至於modCount++這個做用不少,好比用來檢測快速失敗的一種標誌,這個對於咱們目前研究的問題無影響,不用在乎。
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; //將擴充前的elementData大小給oldCapacity int newCapacity = oldCapacity + (oldCapacity >> 1);//newCapacity就是1.5倍的oldCapacity if (newCapacity - minCapacity < 0)//這句話就是適應於elementData就空數組的時候,length=0,那麼oldCapacity=0,newCapacity=0,因此這個判斷成立,在這裏就是真正的初始化elementData的大小了,就是爲10.前面的工做都是準備工做。 newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0)//若是newCapacity超過了最大的容量限制,就調用hugeCapacity,也就是將能給的最大值給newCapacity newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: //新的容量大小已經肯定好了,就copy數組,改變容量大小咯。 elementData = Arrays.copyOf(elementData, newCapacity); }
hugeCapacity();
//這個就是上面用到的方法,很簡單,就是用來賦最大值。 private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); //若是minCapacity都大於MAX_ARRAY_SIZE,那麼就Integer.MAX_VALUE返回,反之將MAX_ARRAY_SIZE返回。由於maxCapacity是三倍的minCapacity,可能擴充的太大了,就用minCapacity來判斷了。 //Integer.MAX_VALUE:2147483647 MAX_ARRAY_SIZE:2147483639 也就是說最大也就能給到第一個數值。仍是超過了這個限制,就要溢出了。至關於arraylist給了兩層防禦。 return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
2)void add(int,E);在特定位置添加元素,也就是插入元素
public void add(int index, E element) { rangeCheckForAdd(index);//檢查index也就是插入的位置是否合理。 //跟上面的分析同樣,具體看上面 ensureCapacityInternal(size + 1); // Increments modCount!! //這個方法就是用來在插入元素以後,要將index以後的元素都日後移一位, System.arraycopy(elementData, index, elementData, index + 1, size - index); //在目標位置上存放元素 elementData[index] = element; size++;//size增長1 }
分析:
rangeCheckForAdd方法
注意:
當調用空的構成函數建立ArrayList時,初始化List大小是在第一次添加時進行。
和添加方法同樣,這幾個刪除方法都是相似的,抽兩個分析下就行,其餘的都差很少。
另外,fastRemove(int)方法是private的,是提供給remove(Object)這個方法用的。
1)remove(int):經過刪除指定位置上的元素
public E remove(int index) { rangeCheck(index);//檢查index的合理性 modCount++;//這個做用不少,好比用來檢測快速失敗的一種標誌。 E oldValue = elementData(index);//經過索引直接找到該元素 int numMoved = size - index - 1;//計算要移動的位數。 if (numMoved > 0) //這個方法也已經解釋過了,就是用來移動元素的。 System.arraycopy(elementData, index+1, elementData, index, numMoved); //將--size上的位置賦值爲null,讓gc(垃圾回收機制)更快的回收它。 elementData[--size] = null; // clear to let GC do its work //返回刪除的元素。 return oldValue; }
2)remove(Object):這個方法能夠看出來,arrayList是能夠存放null值得。
3)clear():將elementData中每一個元素都賦值爲null,等待垃圾回收將這個給回收掉,因此叫clear
4)removeAll(collection c)批量刪除
分析:
batchRemove(xx,xx):
//這個方法,用於兩處地方,若是complement爲false,則用於removeAll若是爲true,則給retainAll()用,retainAll()是用來檢測兩個集合是否有交集的。 private boolean batchRemove(Collection<?> c, boolean complement) { final Object[] elementData = this.elementData; //將原集合,記名爲A int r = 0, w = 0; //r用來控制循環,w是記錄有多少個交集 boolean modified = false; try { for (; r < size; r++) //參數中的集合C一次檢測集合A中的元素是否有, if (c.contains(elementData[r]) == complement) //有的話,就給集合A elementData[w++] = elementData[r]; } finally { // Preserve behavioral compatibility with AbstractCollection, // even if c.contains() throws. //若是contains方法使用過程報異常 if (r != size) { //將剩下的元素都賦值給集合A, System.arraycopy(elementData, r, elementData, w, size - r); w += size - r; } if (w != size) { //這裏有兩個用途,在removeAll()時,w一直爲0,就直接跟clear同樣,全是爲null。 //retainAll():沒有一個交集返回true,有交集但不全交也返回true,而兩個集合相等的時候,返回false,因此不能根據返回值來確認兩個集合是否有交集,而是經過原集合的大小是否發生改變來判斷,若是原集合中還有元素,則表明有交集,而元集合沒有元素了,說明兩個集合沒有交集。 // clear to let GC do its work for (int i = w; i < size; i++) elementData[i] = null; modCount += size - w; size = w; modified = true; } } return modified; }
// 從首開始查找數組裏面是否存在指定元素 public int indexOf(Object o) { if (o == null) { // 查找的元素爲空 for (int i = 0; i < size; i++) // 遍歷數組,找到第一個爲空的元素,返回下標 if (elementData[i]==null) return i; } else { // 查找的元素不爲空 for (int i = 0; i < size; i++) // 遍歷數組,找到第一個和指定元素相等的元素,返回下標 if (o.equals(elementData[i])) return i; } // 沒有找到,返回空 return -1; }
要先檢測因此是否合法。
注意:在get函數中存在element函數,element函數用於返回具體的元素,具體函數以下:
說明:返回的值都通過了向下轉型(Object -> E),這些是對咱們應用程序屏蔽的小細節。
1)arrayList能夠存放null。
2)arrayList本質上就是一個elementData數組。
3)arrayList區別於數組的地方在於可以自動擴展大小,其中關鍵的方法就是gorw()方法。
4)arrayList中removeAll(collection c)和clear()的區別就是removeAll能夠刪除批量指定的元素,而clear是刪除集合中的所有元素。
5)arrayList因爲本質是數組,因此它在數據的查詢方面會很快,而在插入刪除這些方面,性能降低不少,由於須要移動不少數據才能達到應有的效果 6)arrayList實現了RandomAccess,因此在遍歷它的時候推薦使用for循環。