原文地址java
ArrayList是List接口的 可變數組的實現。實現了全部可選列表操做,並容許包括 null
在內的全部元素。除了實現 List
接口外,此類還提供一些方法來操做內部用來存儲列表的數組的大小。ArrayList
繼承自 AbstractList<E>
,這是一個抽象類對一些基礎的list
操做作了一些封裝.實現了RandomAccess 標記接口,代表能夠實現快速隨機訪問.實現了Cloneable
接口的實現表示該容器具備Clone函數操做,Serializable
是序列化。算法
每一個ArrayList
實例都有一個容量,該容量是指用來存儲列表元素的數組的大小。它老是至少等於列表的大小。隨着向ArrayList
中不斷添加元素,其容量也自動增加。自動增加會帶來數據向新數組的從新拷貝,所以,若是可預知數據量的大小,就可在構造ArrayList
實例時指定其容量。數組
在添加大量元素前,應用程序也可使用ensureCapacity
操做來增長ArrayList
實例的容量,這能夠減小遞增式再分配的數量。網絡
注意,此實現不是同步的。若是多個線程同時訪問一個ArrayList實例,而其中至少一個線程從結構上修改了列表,那麼它必須保持外部同步。數據結構
ArrayList這個數據結構比較簡單,整體來講,ArrayList底層結構是數組,他的不少方法都是從數組上面演變而來的。dom
下面咱們先來看一下ArrayList中的一些初始值ide
//經過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 = {}; //被用於默認大小的空實例的共享數組實例。 //與EMPTY_ELEMENTDATA的區別是:當咱們向數組中添加第一個元素時,知道數組該擴充多少。 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * Object[]類型的數組,保存了添加到ArrayList中的元素。ArrayList的容量是該Object[]類型數組的長度 * 當第一個元素被添加時,任何空ArrayList中的elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA將會被 * 擴充到DEFAULT_CAPACITY(默認容量)。 */ transient Object[] elementData; //沒有被私有化是爲了簡化內部類訪問 // ArrayList的大小(指其所含的元素個數) private int size; // 記錄被修改的次數 protected transient int modCount = 0; // 數組的最大值 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 }
elementData
是"Object[] 類型的數組",它保存了添加到ArrayList
中的元素。實際上,elementData
是個動態數組,咱們能經過構造函數 ArrayList(intinitialCapacity)
來執行它的初始容量爲initialCapacity
;若是經過不含參數的構造函數ArrayList()
來建立ArrayList
,則elementData
的容量默認是10。elementData
數組的大小會根據ArrayList
容量的增加而動態的增加,具體的增加方式請看這裏函數
ArrayList
提供了三種方式的構造器。能夠構造一個默認初始容量爲10的空列表、構造一個指定初始容量的空列表以及構造一個包含指定collection
的元素的列表。ui
這些元素按照該collection的迭代器返回的順序排列的。this
// 構造一個指定初始容量的空列表 public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { // 若是給定的初始容量爲負值 throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } // 構造一個默認初始容量爲10的空列表 public ArrayList() { //這裏並無初始化,jdk 1.8以後是在進行add操做後初始化 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } // 構造一個包含指定collection的元素的列表,這些元素按照該collection的迭代器返回的順序排列的 public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray()可能不會正確地返回一個 Object[]數組,那麼使用Arrays.copyOf()方法 if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // 若是指定的collection爲空 this.elementData = EMPTY_ELEMENTDATA; } }
使用無參構造器,默認初始容量爲何是10?
1) 初始時:this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; size = 0;
2) 向數組中添加第一個元素時,add(E e)方法中調用了ensureCapacityInternal(size + 1)方法,即ensureCapacityInternal(1);
3) 在ensureCapacityInternal(int minCapacity)方法中,minCapacity=DEFAULT_CAPACITY=10,而後再調用ensureExplicitCapacity(minCapacity)方法,即ensureExplicitCapacity(10);
4) 在ensureExplicitCapacity(minCapacity)方法中調用grow(minCapacity)方法,即grow(10),此處爲真正具體的數組擴容的算法,在此方法中,經過elementData = Arrays.copyOf(elementData, 10)具體實現了elementData數組初始容量爲10的構造。
// 在數組末尾加上一個元素 public boolean add(E e) { ensureCapacityInternal(size + 1); // 進行擴容檢查 elementData[size++] = e; return true; } // 在數組的指定位置添加元素 public void add(int index, E element) { rangeCheckForAdd(index); // 檢查index是否越界 ensureCapacityInternal(size + 1); // 進行擴容檢查 // 對數據進行復制操做,空出index位置,並插入element,將源數組中從index位置開始後的size-index個元素統一後移一位 System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; // 元素個數加1 } // 按照指定collection集合的迭代器所返回的元素順序,將該collection中的全部元素添加到列表的尾部 public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); // 將collection轉換爲數組類型 int numNew = a.length; // collection中的元素個數 ensureCapacityInternal(size + numNew); // 進行擴容檢查 System.arraycopy(a, 0, elementData, size, numNew); // 將數組a[0,...,numNew-1]複製到數組elementData[size,...,size+numNew-1] size += numNew; return numNew != 0; } // 按照指定collection集合的迭代器所返回的元素順序,將該collection中的全部元素添加到列表的指定位置 public boolean addAll(int index, Collection<? extends E> c) { rangeCheckForAdd(index); // 檢查index是否越界 Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // 進行擴容檢查 // 將數組elementData[index,...,index+numMoved-1]複製到elementData[index+numMoved,...,index+2*numMoved-1] //將源數組中從index位置開始的後numMoved個元素統一後移numNew位 int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); // 將數組a[0,...,numNew-1]複製到數組elementData[index,...,index+numNew-1]完成數據的插入 System.arraycopy(a, 0, elementData, index, numNew); size += numNew; return numNew != 0; }
// 用於自定義設置ArrayList的容量 public void ensureCapacity(int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } // 進行擴容檢查 private void ensureCapacityInternal(int minCapacity) { //第一次add操做初始化,若是爲空ArrayList,那麼初始化容量爲10 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } //判斷是否須要擴容 ensureExplicitCapacity(minCapacity); } //判斷是否須要擴容 private void ensureExplicitCapacity(int minCapacity) { //modCount這個參數運用到了 fail-fast 機制 modCount++; if (minCapacity - elementData.length > 0) grow(minCapacity); // 擴容 } // 擴容 private void grow(int minCapacity) { int oldCapacity = elementData.length; //newCapacity爲之前的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //判斷容量是否到達long int 最大臨界值 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 對數組進行復制處理 elementData = Arrays.copyOf(elementData, newCapacity); } // 檢查是否超過最大容量 0x7fffffff ,是否拋出異常 private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
// 刪除指定位置的元素 public E remove(int index) { rangeCheck(index); //數組越界檢查 modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; //計算數組須要複製的數量 if (numMoved > 0) //將index後的數據都向前移一位 System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; //help GC return oldValue; } // 刪除指定內容的元素(只刪除第一個匹配成功的) public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; } //找到對應的元素後,刪除。刪除元素後的元素都向前移動一位 private void fastRemove(int index) { modCount++; int numMoved = size - index - 1; if (numMoved > 0) // 將index後面的元素總體向前移動一位 System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // help GC } //清空ArrayList,將所有的元素設爲null public void clear() { modCount++; for (int i = 0; i < size; i++) // help GC elementData[i] = null; size = 0; } //刪除ArrayList中從fromIndex到toIndex(區間--左閉右開)之間全部的元素 protected void removeRange(int fromIndex, int toIndex) { modCount++; int numMoved = size - toIndex; //需向前移動的元素的個數 System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved); // help GC int newSize = size - (toIndex-fromIndex); for (int i = newSize; i < size; i++) { elementData[i] = null; } size = newSize; } //刪除ArrayList中包含在指定容器c中的全部元素 public boolean removeAll(Collection<?> c) { Objects.requireNonNull(c); //檢查指定的對象c是否爲空 return batchRemove(c, false); } //移除ArrayList中不包含在指定容器c中的全部元素,與removeAll(Collection<?> c)正好相反 public boolean retainAll(Collection<?> c) { Objects.requireNonNull(c); //檢查指定的對象c是否爲空 return batchRemove(c, true); } // 根據complement的值刪除元素 private boolean batchRemove(Collection<?> c, boolean complement) { final Object[] elementData = this.elementData; int r = 0, w = 0; //讀寫雙指針 w是從新存元素時的索引,r是原來的索引 boolean modified = false; try { //遍歷數組,並檢查這個集合是否包含對應的值,移動要保留的值到數組前面,w最後值爲要保留的元素的數量 //簡單點:若保留,就將相同元素移動到前段;若刪除,就將不一樣元素移動到前段 for (; r < size; r++) if (c.contains(elementData[r]) == complement) //判斷指定容器c中是否含有elementData[r]元素 elementData[w++] = elementData[r]; }finally {//確保異常拋出前的部分能夠完成指望的操做,而未被遍歷的部分會被接到後面 //r!=size表示可能出錯了:c.contains(elementData[r])拋出異常 if (r != size) { System.arraycopy(elementData, r,elementData, w,size - r); w += size - r; } //若是w==size:表示所有元素都保留了,因此也就沒有刪除操做發生,因此會返回false;反之,返回true,並更改數組 //而w!=size的時候,即便try塊拋出異常,也能正確處理異常拋出前的操做,由於w始終爲要保留的前段部分的長度,數組也不會所以亂序 if (w != size) { for (int i = w; i < size; i++) elementData[i] = null; modCount += size - w;//改變的次數 size = w; //新的大小爲保留的元素的個數 modified = true; } } return modified; }
removeAll和retainAll方法:實現刪除或保留ArrayList
中包含Collection c中的的元素。
這兩個方法都用到batchRemove
方法(boolean complement
使得batchRemove
方法獲得了重用)
下面以removeAll爲例,分析batchRemove(c, false)
遍歷elementData
若是集合c中包含elementData
的元素e,則c.contains(elementData[r])爲true
,if不成立,if結束;若是c不包含elementData
的元素e,則if成立,將此元素e賦值給elementData[w++] (即elementData
保留了c中沒有的元素,也就是刪除了c中存在的全部元素。)
執行finally
finally
是無論try中結果如何都會執行的。if(r!=size),則將elementData
未參加比較的元素arraycopy
到elementData
後面;新索引w加上剛arraycopy
的數目;if (w != size),此時w還不等於size
,則將w後的元素移除.只有執行了if (w != size)(事實上只要c中含有elementData的元素,w確定不等於size),才令modified = true,才說明remove成功,返回true,不然返回false。
ArrayList中還有一個用於節約數組內存空間,縮小容量的方法
// 由於容量經常會大於實際元素的數量。內存緊張時,能夠調用該方法刪除預留的位置,調整容量爲元素實際數量。 // 若是肯定不會再有元素添加進來時也能夠調用該方法來節約空間 public void trimToSize() { modCount++; // length是數組長度,size表示數組內元素個數 // size<length那麼就說明數組內有空元素,進行縮小容量操做 if (size < elementData.length) { elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }
去掉預留元素的位置。返回一個新數組,新數組不含null,數組的size和elementData.length相等,以節省空間。此函數可避免size很小但elementData.length很大的狀況。
ArrayList會每次增加會預申請多一點空間,1.5倍,這樣就會出現當size() = 10的時候,ArrayList已經申請了15空間, trimToSize就是刪除多餘的5,只留10。
或許有人會有疑問:
調用Arrays.copyOf複製size長度的元素到elementData,並且由源碼看應該是從0複製到size處,那麼若是我以前調用過add(int index, E element)呢?好比,list={1,2,3,null,null,4,null,null},若是調用trimToSize返回的應該是list={1,2,3,null}(由於size=4)。其實上面這種狀況不會發生的,由於調用add(int index, E element)時,會檢查index的合法性,因此list的元素確定是相鄰的,而不會出現上述這種中間出現null的狀況。
// 將指定位置的元素改成指定的值 public E set(int index, E element) { rangeCheck(index); // 檢查index是否越界 E oldValue = elementData(index); elementData[index] = element; return oldValue; }
//判斷ArrayList中是否包含Object(o) public boolean contains(Object o) { return indexOf(o) >= 0; } //返回一個值在數組首次出現的位置,會根據是否爲null使用不一樣方式判斷。不存在就返回-1。時間複雜度爲O(N) 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; } //返回一個值在數組最後一次出現的位置,不存在就返回-1。時間複雜度爲O(N) public int lastIndexOf(Object o) { if (o == null) { for (int i = size-1; i >= 0; i--) if (elementData[i]==null) return i; } else { for (int i = size-1; i >= 0; i--) if (o.equals(elementData[i])) return i; } return -1; } //返回指定位置的值,由於是數組,因此速度特別快 @SuppressWarnings("unchecked") E elementData(int index) { return (E) elementData[index]; } //返回指定位置的值,可是會檢查這個位置數否超出數組長度 public E get(int index) { rangeCheck(index); return elementData(index); //實質上return (E) elementData[index] }
//保存數組實例的狀態到一個流(即它序列化)。寫入過程數組被更改會拋出異常 private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ int expectedModCount = modCount; s.defaultWriteObject(); //執行默認的反序列化/序列化過程。將當前類的非靜態和非瞬態字段寫入此流 // 寫入大小 s.writeInt(size); // 按順序寫入全部元素 for (int i=0; i<size; i++) { s.writeObject(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } //上面是寫,這個就是讀了。 private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { elementData = EMPTY_ELEMENTDATA; // 執行默認的序列化/反序列化過程 s.defaultReadObject(); // 讀入數組長度 s.readInt(); if (size > 0) { ensureCapacityInternal(size); Object[] a = elementData; //讀入全部元素 for (int i=0; i<size; i++) { a[i] = s.readObject(); } } }
爲何要自定義序列化、反序列化機制呢?
因爲ArrayList實質上是一個動態數組,每每數組中會有空餘的空間,若是採用默認的序列化機制,那些空餘的空間會做爲null寫入本地文件或者在網絡中傳輸,耗費了沒必要要的資源。因此,ArrayList使用自定義序列化機制,僅寫入索引爲【0,size)的有效元素以節省資源
//返回ListIterator,開始位置爲指定參數 public ListIterator<E> listIterator(int index) { if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: "+index); return new ListItr(index); } //返回ListIterator,開始位置爲0 public ListIterator<E> listIterator() { return new ListItr(0); } //返回普通迭代器 public Iterator<E> iterator() { return new Itr(); } //通用的迭代器實現 private class Itr implements Iterator<E> { int cursor; //遊標,下一個元素的索引,默認初始化爲0 int lastRet = -1; //上次訪問的元素的位置 int expectedModCount = modCount;//迭代過程不容許修改數組,不然就拋出異常 //是否還有下一個 public boolean hasNext() { return cursor != size; } //下一個元素 @SuppressWarnings("unchecked") public E next() { checkForComodification();//檢查數組是否被修改 int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; //向後移動遊標 return (E) elementData[lastRet = i]; //設置訪問的位置並返回這個值 } //刪除元素 public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification();//檢查數組是否被修改 try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } cursor = i; lastRet = i - 1; checkForComodification(); } //檢查數組是否被修改 final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } } //ListIterator迭代器實現 private class ListItr extends Itr implements ListIterator<E> { ListItr(int index) { super(); cursor = index; } public boolean hasPrevious() { return cursor != 0; } public int nextIndex() { return cursor; } public int previousIndex() { return cursor - 1; } @SuppressWarnings("unchecked") public E previous() { checkForComodification(); int i = cursor - 1; if (i < 0) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i; return (E) elementData[lastRet = i]; } public void set(E e) { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.set(lastRet, e); } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } public void add(E e) { checkForComodification(); try { int i = cursor; ArrayList.this.add(i, e); cursor = i + 1; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } }
Iterator與ListIterator的區別:
//返回ArrayList的大小(元素個數) public int size() { return size; } //判斷ArrayList是否爲空 public boolean isEmpty() { return size == 0; } //返回此 ArrayList實例的淺拷貝(元素自己沒有被複制,複製過程數組發生改變會拋出異常) public Object clone() { try { ArrayList<?> v = (ArrayList<?>) super.clone(); v.elementData = Arrays.copyOf(elementData, size); v.modCount = 0; return v; } catch (CloneNotSupportedException e) { throw new InternalError(e); } } /* 淺克隆就是咱們所看到的Arrays.copyOf, System.arraycopy,數組是新的,可是裏面N個元素全是引用的舊的。 淺拷貝(影子克隆):只複製基本類型。 深拷貝(深度克隆):基本類+對象。 */ //返回一個包含ArrayList中全部元素的數組 public Object[] toArray() { return Arrays.copyOf(elementData, size); } // 返回一個數組,使用運行時肯定類型,該數組包含在這個列表中的全部元素(從第一到最後一個元素) // 返回的數組容量由參數和本數組中較大值肯定 @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { if (a.length < size) // Make a new array of a's runtime type, but my contents: return (T[]) Arrays.copyOf(elementData, size, a.getClass()); System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; }
主要是考慮到不一樣的JVM,有的VM會在加入一些數據頭,當擴容後的容量大於MAX_ARRAY_SIZE,咱們會去比較最小須要容量和MAX_ARRAY_SIZE作比較,若是比它大, 只能取Integer.MAX_VALUE,不然是Integer.MAX_VALUE -8。
這個是從jdk1.7開始纔有的
jdk1.6 public ArrayList() { this(10); } jdk1.7 public ArrayList() { super(); this.elementData = EMPTY_ELEMENTDATA; } jdk1.8 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
對比下能夠看出:jdk1.6的無參構造方法(默認構造方法)構造的ArrayList的底層數組elementData大小(容量)默認爲10;從1.7開始,無參構造方法構造的ArrayList的底層數組elementData大小默認爲0。
java集合類在jdk1.7版本基本上都有一種改動:懶初始化。懶初始化指的是默認構造方法構造的集合類,佔據儘量少的內存空間(對於ArrayList來講,使用空數組來佔據儘可能少的空間,不使用null是爲了不null判斷),在第一次進行包含有添加語義的操做時,才進行真正的初始化工做。
1.7開始的ArrayList,默認構造方法構造的實例,底層數組是空數組,容量爲0,在進行第一次add/addAll等操做時纔會真正給底層數組賦非empty的值。若是add/addAll添加的元素小於10,則把elementData數組擴容爲10個元素大小,不然使用恰好合適的大小(例如,第一次addAll添加6個,那麼擴容爲10個,第一次添加大於10個的,好比24個,擴容爲24個,恰好合適)
1.8版本,默認構造的實例這個行爲沒有改變,只是用的數組名字變了。
(因爲jdk1.7和jdk1.8在擴容算法方面差異不大,因此下面沒有嚴格區分)
jdk1.6 public void ensureCapacity(int minCapacity) { modCount++; int oldCapacity = elementData.length; if (minCapacity > oldCapacity) { Object oldData[] = elementData; int newCapacity = (oldCapacity * 3)/2 + 1; if (newCapacity < minCapacity) newCapacity = minCapacity; // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); } }
從上面的代碼能夠看出jdk1.6的ensureCapacity方法只是簡單進行了邏輯上的操做,沒有過多考慮int型溢出的問題,從1.7開始對這個進行了完善。
並且沒考慮入參minCapacity可能由於int溢出變爲負數。這個方法能夠外部手動調用,手動擴容傳入負數這個確定是應該攔截掉的。可是自動擴容會由於int溢出產生負數,碰到這種狀況時應該特殊處理,而不是什麼都不作,等着後面拋出一個ArrayIndexOutOfBoundsException。
還有就是下面這句代碼會形成過早溢出
int newCapacity = (oldCapacity * 3)/2 + 1;
雖然上面這行代碼和1.7開始的oldCapacity + (oldCapacity >> 1) 差很少,都是至關於1.5倍,但其實是有
區別的。
這裏主要有兩個區別
第一個區別是jdk1.6的乘除運算的數學結果比後面一個大1好比oldCapacity=10,1.6的
算法獲得16,1.7開始的算法獲得15,這個影響不大;
第二個區別就是二者在數字比較大時運算結果不同,好比
oldCapacity=10^9,這個數和Integer.MAX_VALUE位數同樣,用1.6的算法獲得的會是錯誤的-647483647,用
1.7的則是正確的1500000000,這時候明明能夠1.5倍擴容,可是jdk1.6卻用的是按需擴容。
ensureCapacity(稱之爲手動,是由於此方法是public的,能夠外部手動調用)。
在1.6版本是隻有這個手動的方法,內部自動操做也是調用這個方法,1.7開始進行了區分,而且進一步改進了擴容操做。
newCapacity = oldCapacity + (oldCapacity >> 1);
這行代碼不只僅是使用位運算加快執行速度,上面說了,這種作法纔是對的,是真正的1.5倍。不只僅由於那一個大小的差異,更重要的是避免過早出現int溢出的狀況,保證了內部自動擴容會盡可能按規定的策略執行。同時整個擴容處理流程中多增長了幾處if判斷,對各類狀況處理更加完善。
這種算法構造出來的新的數組長度的增量都會比上一次大( 並且是愈來愈大) ,避免頻繁newInstance 的狀況。
由上面分析的增長刪除方法能夠看出在ArrayList中常常會調用 System.arraycopy 這個效率很低的操做來複制數組,因此致使ArrayList在插入和刪除操做中效率不高。