做者將在本文詳細贅述平常開發中最經常使用集合類-ArrayList,本次JCF源碼分析基於JDK1.7,主要從如下幾個方向分析:java
UML類圖關係算法
數據結構數組
接口介紹安全
經常使用、重要方法的實現數據結構
(UML類圖
)框架
從UML關係類圖,咱們能夠直觀的看出ArrayList的類結構,圖中虛線表示實現(implements
)關係,實線表示繼承(extends
)關係,咱們沒必要在還不熟悉的狀況下對這種關係進行「死記硬背」,由於在理解整個集合框架以後天然不用看都會比較清晰,辣麼下面咱們將對接口或類一一介紹:dom
在介紹Collection具體的實現類或者某一種抽象以前,筆者須要對JAVA最經常使用的數據結構進行簡單贅述工具
線性結構:線性結構的元素在物理內存上有序/連續的分佈,使其可使用下標(Index
)快速訪問,查找複雜度爲O(1),如:數組源碼分析
鏈表結構:鏈表結構的元素在物理內存上無序/隨機的分佈,當前元素保留上一個或下一個元素的引用叫單向鏈表,保留上一個且下一個元素的引用叫雙向鏈表 ,鏈表中第一個元素的上一個索引位置指向最後一個元素且最後一個元素的下一個索引位置指向第一個元素叫循環鏈表(簡單理解成一根繩子頭尾相連成圓
),查詢複雜度爲O(logn),如:LinkedList、LinkedHashMap性能
散列結構:
以上是筆者以爲最多見的一些數據結構,筆者會在後續介紹Collection不一樣抽象的時候講解集合每一種API對應的數據結構是哪種,理解這些數據結構的結構及優缺點有助於讀者在根據具體的場景下選用是和數據結構的API提升性能,固然JDK裏面還有一些跳錶、樹、圖的數據結構實現,由於文章是介紹經常使用的集合類,因此就不一一介紹了,若是讀者有興趣能夠自行學習
在這以前筆者但願讀者對接口的理解能夠上升到一個邏輯層次,而不是僅僅知道實現了接口就必須實現接口的方法,好比Iterable接口是可迭代的語義,那麼子類就必須是具有可迭代需求才能實現該接口,不然就屬於一種錯誤的設計,再看看JDK中沒有任何方法的Serializable
、以及上圖中出現的RandomAccess
這些接口的出現自己就是約束着一種邏輯定義,筆者在這裏稱之爲接口的語義
集合頂層接口,接口的語義標誌實現類是可迭代的(循環
),全部須要被for(String str : strs)
語法迭代的支持都必須實現此接口(數組除外
),接口方法就一個Iterator<T> iterator();
此方法返回一個迭代器(Iterator接口
)的實現類。
幾乎是JAVA全部數據結構集合的接口(二元散列數據結構除外,如:Map
),接口的語義標誌實現類必須爲集合,集合的解釋也就是:「一堆東西」。集合裏的「東西」,叫做元素(element
),既然是一堆東西而咱們目的是要對這堆東西進行操做,天然Collection的接口就須要約束每一個集合都必須有如下方法:
size()
: 集合中元素的個數
contains(Object element)
: 集合中是否包含某個元素
iterator()
: 集合的迭代器
add(Object element)
:往集合裏添加元素
remove(Object elemnt)
:從集合裏面移除元素
理論上一個簡單的集合接口有以上方法就能夠正常工做,可是爲了操做方便JCF做者Josh Bloch增長了如下幾個方法的約束:
isEmpty()
:集合是否爲空
toArray()
:轉換成數組
containsAll(Collection collection)
:集合是否包含collection變量中的全部元素
addAll(Collection collection)
:添加collection中全部元素到當前集合
removeAll(Collection collection)
:移除全部在collection中的元素
retainAll(Collection collection)
:取兩個集合的交集
clear()
:清除集合全部元素
有了以上方法的約束可讓咱們在使用集合的時候操做更方便
介紹了經常使用數據結構,接下來咱們來看Collection的第一種集合抽象——有序List列表,List有以上兩種數據結構的實現: 線性結構(ArrayList
)、鏈表結構(LinkedList
),本文咱們將贅述線性結構的實現(ArrayList
),List接口在extends了Collection以後,增長了對有序列表操做的幾個方法約束:
get(int i)
:獲取i位置的元素
set(int i,Object element)
:替換集合中的i位置插入element元素,返回原位置的元素
add(int i,Object element)
:集合i位置插入element,i位置之後的元素向後移一位
remove(int i)
:移除集合中i位置的元素
indexOf(Object element)
:查找element並返回元素在集合的下標索引,若是沒有返回-1
lastIndexOf(Object element)
:查找element元素最後一次在集合中的下標索引,若是沒有返回-1
listIterator()
:建立一個集合的迭代器
listIterator(int i)
:從i位置建立一個List集合的迭代器
subList(int star,int end)
:返回當前集合的一個子集,從當前集合的start位置到end位置,注意此子集並非新new出來的對象,因此對子集的操做會影響到當前集合。
以上方法都是針對有序集合的操做,根據有序集合的特性約束一系列依靠下標索引(Index
)的操做
前面提到過該接口,此接口沒有任何方法,存在的意義僅僅是爲了表示邏輯上的一種語義——實現了該接口的子類應該具備一個特性:代表其支持快速(一般是固定複雜度)隨機訪問。此接口的主要目的是容許通常的算法(二分法
、快速排序
...)更改其行爲,從而在將其應用到隨機或連續訪問列表時能提供良好的性能,因此遵循了該接口語義的實現類使用for(int i=0;i<xx;i++)
會比使用Iterator
迭代器速度要快,如:ArrayList根據下標索引直接訪問任何位置的元素。
此接口沒有任何方法,存在的意義僅僅是爲了表示邏輯上的一種語義——實現類可被序列化的。
此接口沒有任何方法,存在的意義僅僅是爲了表示邏輯上的一種語義——實現類可被克隆的。
介紹完接口以後,咱們來看下UML類圖中AbstrctCollection
、AbstractList
、ArrayList
的具體實現類,在講述過程當中筆者會省去那些非重點的方法,好比toString()
、isEmpty()
等方法。
做爲Collection的抽象類及幾乎全部集合類的父類來講(筆者說的是幾乎,好比Map就非其子類
),理應實現全部Collection接口中約束的最基本方法,但有些約束是要在具體場景下才能根據具體需求進行實現,如:add()
須要把對象添加到具體的數據結構中,能夠是數組(線性
)、Node(鏈表
),因此 AbstractCollection中主要實現瞭如下方法:
contains(Object var1)方法實現
/** * 方法做用:集合中是否包含var1元素 * 實現方式:使用迭代器進行迭代比對,若是先相同返回 */ public boolean contains(Object var1) { Iterator var2 = this.iterator();//抽象方法,具體在子類中會實現 if(var1 == null) { while(var2.hasNext()) { if(var2.next() == null) { return true; } } } else { while(var2.hasNext()) { if(var1.equals(var2.next())) { return true; } } } return false; }
toArray()方法實現
/** * 方法做用:集合轉成數組 * 實現方式:new一個集合大小一致的數組,而後用集合迭代器迭代複製到新數組,這個地方的複製 * 使用的Arrays.copy()方法,這個方法在整個線性結構的集合裏面大量存在,幾乎所 * 全部的集合修改操做都使用到了它,而這個方法底層的實現來自於一個JNI方法, * System.arrayCopy(),因爲是本地方法因此效率很是高 */ public Object[] toArray() { Object[] var1 = new Object[this.size()]; Iterator var2 = this.iterator(); for(int var3 = 0; var3 < var1.length; ++var3) { if(!var2.hasNext()) { return Arrays.copyOf(var1, var3);//線性結構的集合中大量使用的方法 } var1[var3] = var2.next(); } /* finishToArray(var1,va2)方法是當new的這個數組大小不足集合大小時完成多餘的賦值 * 那麼,明明是根據集合size new出來的數組爲何會放不下呢,由於極可能在複製的過程 * 中集合被修改了。 */ return var2.hasNext()?finishToArray(var1, var2):var1; }
add(E var1)方法實現,前面說過了不一樣子類有不一樣的實現
public boolean add(E var1) {throw new UnsupportedOperationException();}
remove(Object var1)方法實現
/** * 方法做用:從集合中移除var1元素 * 實現方式:獲取迭代器,使用迭代器中的remove刪除元素 */ public boolean remove(Object var1) { Iterator var2 = this.iterator();//抽象方法,具體在子類中會實現 if(var1 == null) { while(var2.hasNext()) { if(var2.next() == null) { var2.remove();//調用迭代器的remove return true; } } } else { while(var2.hasNext()) { if(var1.equals(var2.next())) { var2.remove(); return true; } } } return false; }
containsAll(Collections<?> var1);
/** * 方法做用:集合中是否包含全部var1集合中的元素 * 實現方式:寫法能夠借鑑,若是是你會這樣寫嗎?是否是代碼整潔一些呢 */ public boolean containsAll(Collection<?> var1) { Iterator var2 = var1.iterator(); Object var3; do { if(!var2.hasNext()) { return true; } var3 = var2.next(); } while(this.contains(var3));//調用上面的contains()方法,因此是兩層循環 return false; }
addAll(Collection<? extends E> var1)方法實現
/** * 方法做用:把var1集合中的全部數據添加到集合 * 實現方式:沒什麼好說的~~ */ public boolean addAll(Collection<? extends E> var1) { boolean var2 = false; Iterator var3 = var1.iterator(); while(var3.hasNext()) { Object var4 = var3.next(); if(this.add(var4)) { //調用上面的add()方法,實際是子類中實現的add()方法 var2 = true; } } return var2; }
retainAll(Collection<?> var1)
/** * 方法做用:刪除當前集合在var1集合中存在的元素(集合交集,可是會把原集合數據從內存刪除) * 實現方式:代碼很好懂,很少說,只要刪除過一個元素 返回True,不然False */ public boolean retainAll(Collection<?> var1) { boolean var2 = false; Iterator var3 = this.iterator(); while(var3.hasNext()) { if(!var1.contains(var3.next())) { var3.remove(); var2 = true; } } return var2; }
AbstractCollection總結:雖然抽象類中實現了不少方法,但實際仍是基於抽象方法之上的,具體的業務邏輯都在子類實現的抽象方法中,而AbstractCollection中實現的是與業務不要緊的公共代碼,或者說最原始、最基本的實現,好比:全部查找都是使用的迭代器,實際上咱們以前介紹過RandomAccess接口的語義,因此遵循了該接口語義的實現類使用
for(int i=0;i<xx;i++)
會比使用Iterator
迭代器速度要快,後面咱們會看到ArrayList對contains()
方法進行了重寫
indexOf(Object o)方法實現
/** * 方法做用:查找元素o在集合中的位置,沒有則返回-1 * 實現方式:利用迭代器進行查詢 */ public int indexOf(Object o) { ListIterator<E> it = listIterator(); if (o==null) { while (it.hasNext()) if (it.next()==null) return it.previousIndex(); } else { while (it.hasNext()) if (o.equals(it.next())) return it.previousIndex(); } return -1; }
iterator()方法實現,生成按從前到後順序迭代的迭代器
public Iterator<E> iterator() { return new AbstractList.Itr();//生成迭代器,即下面的迭代器內部類實現代碼 } /** * 迭代器內部類實現,此迭代器只有next()獲取下一個元素的方法,因此迭代的順序是從前到後 */ private class Itr implements Iterator<E> { int cursor; //下一個元素索引 int lastRet; //調用next()方法返回對象的下標索引 int expectedModCount; //模數:集合每次的修改都會+1,用來判斷在迭代過程當中集合是否修改過, //下面會會詳細介紹 private Itr() { this.cursor = 0; //初始化迭代器的時候下一個元素索引從0開始 this.lastRet = -1;//默認-1 this.expectedModCount = AbstractList.this.modCount;//賦值爲集合的模數 } public boolean hasNext() { //若是當前索引 != 集合大小 說名還有下一個 return this.cursor != AbstractList.this.size(); } public E next() { //獲取下一個的時候,校驗迭代器模數是否==集合模數,若是不等於則證實集合在迭代器 //生成以後被修改過 this.checkForComodification(); try { int var1 = this.cursor; Object var2 = AbstractList.this.get(var1); this.lastRet = var1;//var2對象的下標索引 this.cursor = var1 + 1; //這部分代碼很好理解,返回下一個 當前索引+1 return var2; } catch (IndexOutOfBoundsException var3) { this.checkForComodification(); throw new NoSuchElementException(); } } public void remove() { if(this.lastRet < 0) { throw new IllegalStateException(); } else { //校驗迭代器模數是否==集合模數,若是不等於就說明集合在迭代器生成以後被修改過 this.checkForComodification(); try { AbstractList.this.remove(this.lastRet);//remove當前index的元素 if(this.lastRet < this.cursor) { --this.cursor; //刪除成功後,下一個元素爲-1 } //重置-1 this.lastRet = -1; //由於作了修改集合大小的操做,因此從新給模數賦值 this.expectedModCount = AbstractList.this.modCount; } catch (IndexOutOfBoundsException var2) { throw new ConcurrentModificationException(); } } } /** * 校驗迭代器模數是否==集合模數,若是不等於則證實集合在迭代器生成以後被修改過 * 若是集合被修改過,說名迭代器失效,拋出異常 */ final void checkForComodification() { if(AbstractList.this.modCount != this.expectedModCount) { throw new ConcurrentModificationException(); } } }
listIterator()方法實現
public ListIterator<E> listIterator(int var1) { //校驗索引var1是否在0到集合size之內,不然IndexOutOfBoundsException this.rangeCheckForAdd(var1); return new AbstractList.ListItr(var1); } /** * 迭代器內部類實現,迭代順序隨意的迭代器,而且在迭代過程當中能夠修改集合 * */ private class ListItr extends AbstractList.Itr implements ListIterator<E> { ListItr(int var2) { super(); this.cursor = var2; } public boolean hasPrevious() { return this.cursor != 0;//當下一個索引 !=0 的時候說名還有上一個 } public E previous() { //返回上一個元素 this.checkForComodification(); try { int var1 = this.cursor - 1; Object var2 = AbstractList.this.get(var1); this.lastRet = this.cursor = var1; return var2; } catch (IndexOutOfBoundsException var3) { this.checkForComodification(); throw new NoSuchElementException(); } } public int nextIndex() { return this.cursor;//返回下一個元素的索引 } public int previousIndex() { return this.cursor - 1;//返回上一個元素的索引 } public void set(E var1) { if(this.lastRet < 0) { throw new IllegalStateException(); } else { this.checkForComodification(); try { AbstractList.this.set(this.lastRet, var1);//把元素var1替換當前位置的值 this.expectedModCount = AbstractList.this.modCount; } catch (IndexOutOfBoundsException var3) { throw new ConcurrentModificationException(); } } } public void add(E var1) { this.checkForComodification(); try { int var2 = this.cursor; AbstractList.this.add(var2, var1);//添加元素,而且把模數更新 this.lastRet = -1; this.cursor = var2 + 1; this.expectedModCount = AbstractList.this.modCount; } catch (IndexOutOfBoundsException var3) { throw new ConcurrentModificationException(); } } }
AbstractList總結:主要實現了List接口中的以上3個方法,注意此處
indexOf()
仍然使用的是迭代器遍歷的,由於List的數據結構分爲線性和鏈表結構,下面咱們能夠看到線性結構的ArrayList實現RandomAccess
接口後對indexOf()
進行了重寫
Itr迭代器實現
只能向後迭代,而且在迭代過程當中不能對集合元素進行add、set操做,但能夠removeListItr迭代器實現
1.ListIterator和Iterator都有hasNext()和next()方法,能夠實現順序向後遍歷,可是ListIterator有hasPrevious()和previous()方法,能夠實現逆向(順序向前)遍歷。Iterator不能夠。
2.ListIterator能夠定位當前索引的位置,nextIndex()和previousIndex()能夠實現。Iterator沒有此功能。
3.均可實現刪除操做,可是ListIterator能夠實現對象的修改,set()方法能夠實現修改,add()能夠實現添加,Iterator僅能遍歷,不能修改。
private int size;//集合裏總共包含多少個元素,應該 <= elementData.length;
private transient Object[] elementData; //這行源碼錶示ArrayList底層爲數組實現,那麼既然底層是數組,怎麼實現擴容的集合呢?請看下面這個grow()
方法
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity);//前面提到讓讀者留意的工具類 } /* 再看看Arrays.copy()方法實現,實際上是System.arraycopy方法,這個方法是本地方法,效率會比較快 */ public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));//底層System.arraycopy()實現 return copy; }
前面說過ArrayList會對contains()方法重寫,接下來咱們詳細看下
public boolean contains(Object o) { return indexOf(o) >= 0;//調用的indexOf()方法,咱們還記得這個方法在AbstractList中 //使用迭代器實現過,可是ArrayList又對其重寫了 } /* ArrayList已經使用for(int i=0;i<size;i++)來實現, 回想下前面咱們說過的 * RandomAccess接口的語義就明白這個的用意,不記得的童鞋能夠回到上面看下 */ 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()方法實現
public E get(int index) { rangeCheck(index);//範圍校驗,不在0到size內就IndexOutOfBoundsException return elementData(index);//直接經過數組下標返回值}
set()方法實現
public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index);//拿出index位置的元素 elementData[index] = element;//設置index位置的元素爲新elment return oldValue;//返回原index位置元素 }
add()方法實現
public boolean add(E e) { ensureCapacityInternal(size + 1);//若是elementData數組長度不夠當前size+1,就擴容 elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { if (elementData == EMPTY_ELEMENTDATA) {//若是elementData仍是0,則取minCapacity和默認長度10兩個數中的大值 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; //由於是添加,因此模數須要加1 if (minCapacity - elementData.length > 0) grow(minCapacity);//前面介紹過,若是長度不夠則擴容 }
remove()方法實現
public E remove(int index) { rangeCheck(index); modCount++; //模數自增,筆者反覆強調模數,由於這是一種防止線程安全的措施 E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved);//本地方法copy數組,把index後的全部數組往前挪一個位置 //最後一個位置置爲空,注意看不少JDK源碼中都會寫到 Help GC或者下面這段話, //由於設置引用爲null以後,GC能夠檢查到GC Roots 不可達,從而回收內存 elementData[--size] = null; // clear to let GC do its work return oldValue; }
subList(int fromIndex, int toIndex)方法實現
public List<E> subList(int fromIndex, int toIndex) { subListRangeCheck(fromIndex, toIndex, size);//校驗fromIndex和toIndex是否合法 return new SubList(this, 0, fromIndex, toIndex); } /* 產生一個當前集合從fromIndex位置到toIndex位置的子集合, * 這個地方你們看源碼留意下子集合的產生過程? */ private class SubList extends AbstractList<E> implements RandomAccess { private final AbstractList<E> parent;//保存父集合的引用 private final int parentOffset;//上面的fromIndex private final int offset;//針對父集合的偏移量,後續根據用戶輸入的index進行偏移 int size; SubList(AbstractList<E> parent, int offset, int fromIndex, int toIndex) { this.parent = parent; this.parentOffset = fromIndex; this.offset = offset + fromIndex;//默認賦值爲fromIndex this.size = toIndex - fromIndex; this.modCount = ArrayList.this.modCount; } public E set(int index, E e) { rangeCheck(index); checkForComodification(); E oldValue = ArrayList.this.elementData(offset + index); ArrayList.this.elementData[offset + index] = e; return oldValue; } public E get(int index) { rangeCheck(index); checkForComodification(); //其實就是獲取父集合指定位置的元素,因此子集合並無new出新內存,這點請讀者理解 return ArrayList.this.elementData(offset + index); } public int size() { checkForComodification(); return this.size; } public void add(int index, E e) { rangeCheckForAdd(index); checkForComodification(); parent.add(parentOffset + index, e); this.modCount = parent.modCount; this.size++; } public E remove(int index) { rangeCheck(index); checkForComodification(); E result = parent.remove(parentOffset + index); this.modCount = parent.modCount; this.size--; return result; } protected void removeRange(int fromIndex, int toIndex) { checkForComodification(); parent.removeRange(parentOffset + fromIndex, parentOffset + toIndex); this.modCount = parent.modCount; this.size -= toIndex - fromIndex; } public boolean addAll(Collection<? extends E> c) { return addAll(this.size, c); } public boolean addAll(int index, Collection<? extends E> c) { rangeCheckForAdd(index); int cSize = c.size(); if (cSize==0) return false; checkForComodification(); parent.addAll(parentOffset + index, c); this.modCount = parent.modCount; this.size += cSize; return true; }
ArrayList總結:由於ArrayList的底層是數組實現,因此它是一個線性的結構,它在內存中是一段連續的地址,因此咱們能夠經過Index很快的訪問元素,然而咱們看到了每add或者remove操做都會調用System.arraycopu()複製改動以後的全部元素,因此咱們說隨機插入和刪除操做線性結構沒有鏈表結構效率高(這裏請你們注意,後續講到鏈表結構的時候會作一個對比,看看是否必定線性結構的效率比較高)
而後請你們注意的是subList()方法,上面有說過subList並不是產生一個新對象,而是經過使用下標偏移量取父類數組中的元素,也就是說子集合和父集合其實在內存中是一個集合,因此對子集合的任何修改將直接影響到父集合,咱們不少剛入門的童鞋,可能再使用中會遇到這個問題,因此在這裏提出來
ArrayList講到這裏就已經講完了,由於ArrayList做爲咱們JCF框架源碼分析系列
的第二集,因此裏面着重的介紹了Iterable、Iterator、Collection、AbstractCollection、List、Abstract、RandomAccess等接口或抽象類,後續文章中若有重複的就不在贅述。