1. 程式人生 > >ArrayList初始預設容量(長度)

ArrayList初始預設容量(長度)

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

                       

  每個ArrayList例項都有一個容量,該容量是指用來儲存列表元素的陣列的大小。它總是至少等於列表的大小。隨著向ArrayList中不斷新增元素,其容量也自動增長。自動增長會帶來資料向新陣列的重新拷貝,因此,如果可預知資料量的多少,可在構造ArrayList時指定其容量。在新增大量元素前,應用程式也可以使用ensureCapacity操作來增加ArrayList例項的容量,這可以減少遞增式再分配的數量。 
  
  注意,此實現不是同步的。如果多個執行緒同時訪問一個ArrayList例項,而其中至少一個執行緒從結構上修改了列表,那麼它必須保持外部同步。這通常是通過同步那些用來封裝列表的物件來實現的。但如果沒有這樣的物件存在,則該列表需要運用{@link Collections#synchronizedList Collections.synchronizedList}來進行“包裝”,該方法最好是在建立列表物件時完成,為了避免對列表進行突發的非同步操作。

下面我們討論下ArrayList初始預設容量的問題。
  
有文章說ArrayList預設構造的容量為10,沒錯。 因為ArrayList的底層是由一個Object[]陣列構成的,而這個Object[]陣列,預設的長度是10,所以有的文章會說ArrayList長度容量為10。

然而你所指的size()方法,指的是“邏輯”長度。 
所謂“邏輯”長度,是指記憶體已存在的“實際元素的長度”  而“空元素不被計算”

即:當你利用add()方法,向ArrayList內新增一個“元素”時,邏輯長度就增加1位。 而剩下的9個空元素不被計算。

如下程式碼:

ArrayList<
String> list = new ArrayList<String>();  System.out.println("size = " + list.size());
  • 1
  • 2

輸出結果如下:

size = 0
   
  • 1

ArrayList預設size()是0.

ArrayList原始碼解析

JDK版本不一樣,ArrayList類的原始碼也不一樣。

  • JDK1.8

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 = {};    //被用於預設大小的空例項的共享陣列例項。其與EMPTY_ELEMENTDATA的區別是:當我們向陣列中新增第一個元素時,知道陣列該擴充多少。    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};    /**     * Object[]型別的陣列,儲存了新增到ArrayList中的元素。ArrayList的容量是該Object[]型別陣列的長度     * 當第一個元素被新增時,任何空ArrayList中的elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA將會被     * 擴充到DEFAULT_CAPACITY(預設容量)。     */    transient Object[] elementData; //非private是為了方便巢狀類的訪問    // ArrayList的大小(指其所含的元素個數)    private int size;    ......  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

ArrayList包含了兩個重要的物件:elementData 和 size。

  1. elementData 是”Object[] 型別的陣列”,它儲存了新增到ArrayList中的元素。實際上,elementData是個動態陣列,我們能通過建構函式 ArrayList(int initialCapacity)來執行它的初始容量為initialCapacity;如果通過不含引數的建構函式ArrayList()來建立 ArrayList,則elementData的容量預設是10。elementData陣列的大小會根據ArrayList容量的增長而動態的增長,具 體的增長方式,請參考原始碼分析中的ensureCapacity()函式。

  2. size 則是動態陣列的實際大小。

建構函式

ArrayList提供了三種方式的構造器,可以構造一個預設初始容量為10的空列表、構造一個指定初始容量的空列表以及構造一個包含指定collection的元素的列表,這些元素按照該collection的迭代器返回的順序排列的。

     /**     * 構造一個指定初始容量的空列表     * @param  initialCapacity  ArrayList的初始容量     * @throws IllegalArgumentException 如果給定的初始容量為負值     */    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() {        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;    }    /**     * 構造一個包含指定collection的元素的列表,這些元素按照該collection的迭代器返回的順序排列的     * @param c 包含用於去構造ArrayList的元素的collection     * @throws NullPointerException 如果指定的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)                //Arrays.copyOf()返回一個 Object[].class型別的,大小為size,元素為elementData[0,...,size-1]                elementData = Arrays.copyOf(elementData, size, Object[].class);        } else {            // replace with empty array.            this.elementData = EMPTY_ELEMENTDATA;        }    }
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

ArrayList構造一個預設初始容量為10的空列表:

  1. 初始情況: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的構造。

    調整陣列的容量

      從add()與addAll()方法中可以看出,每當向陣列中新增元素時,都要去檢查新增元素後的個數是否會超出當前陣列的長度,如果超出,陣列將會進行擴容,以滿足新增資料的需求。陣列擴容實質上是通過私有的方法ensureCapacityInternal(int minCapacity) -> ensureExplicitCapacity(int minCapacity) -> grow(int minCapacity)來實現的,但在jdk1.8中,向用戶提供了一個public的方法ensureCapacity(int minCapacity)使使用者可以手動的設定ArrayList例項的容量,以減少遞增式再分配的數量。此處與jdk1.6中直接通過一個公開的方法ensureCapacity(int minCapacity)來實現陣列容量的調整有區別。

    /**     * public方法,讓使用者能手動設定ArrayList的容量     * @param   minCapacity 期望的最小容量     */    public void ensureCapacity(int minCapacity) {        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)            // any size if not default element table            ? 0            // larger than default for default empty table. It's already            // supposed to be at default size.            : DEFAULT_CAPACITY;        if (minCapacity > minExpand) {            ensureExplicitCapacity(minCapacity);        }    }    private void ensureCapacityInternal(int minCapacity) {        //當elementData為空時,ArrayList的初始容量最小為DEFAULT_CAPACITY(10)        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);        }        ensureExplicitCapacity(minCapacity);    }    private void ensureExplicitCapacity(int minCapacity) {        modCount++;        // overflow-conscious code        if (minCapacity - elementData.length > 0)            grow(minCapacity);    }    //陣列可被分配的最大容量;當需要的陣列尺寸超過VM的限制時,可能導致OutOfMemoryError    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;    /**     * 增加陣列的容量,確保它至少能容納指定的最小容量的元素量     * @param minCapacity 期望的最小容量     */    private void grow(int minCapacity) {        // overflow-conscious code        int oldCapacity = elementData.length;        //注意此處擴充capacity的方式是將其向右一位再加上原來的數,實際上是擴充了1.5倍        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);    }    private static int hugeCapacity(int minCapacity) {        if (minCapacity < 0) // overflow            throw new OutOfMemoryError();        return (minCapacity > MAX_ARRAY_SIZE) ?            Integer.MAX_VALUE :            MAX_ARRAY_SIZE;    }
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

附:jdk1.6中ensureCapacity(int minCapacity)方法:

// 確定ArrarList的容量。// 若ArrayList的容量不足以容納當前的全部元素,設定 新的容量=“(原始容量x3)/2 + 1”    public void ensureCapacity(int minCapacity) {        modCount++;  // 將“修改統計數”+1        int oldCapacity = elementData.length;        if (minCapacity > oldCapacity) {            Object oldData[] = elementData;            int newCapacity = (oldCapacity * 3)/2 + 1;            if (newCapacity < minCapacity)                newCapacity = minCapacity;            elementData = Arrays.copyOf(elementData, newCapacity);        }    }
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

為什麼ArrayList自動容量擴充選擇擴充1.5倍?

這種演算法構造出來的新的陣列長度的增量都會比上一次大( 而且是越來越大) ,即認為客戶需要增加的資料很多,而避免頻繁newInstance 的情況。

ArrayList實現原理以及其在jdk1.6和jdk1.7的實現區別

public class ArrayList<E> extends AbstractList<E>          implements List<E>, RandomAccess, Cloneable, java.io.Serializable  
   
  • 1
  • 2

可以看到ArrayLis < E>是支援泛型的,所以ArrayList可以構造成任何型別的動態陣列。

同時它也繼承了AbstractList抽象類,抽象類實現了很多預設的方法,但是還有一些方法還是抽象方法。

實現了通用的List列表介面,這裡面定義了List列表的基礎方法。
同時實現了RandomAccess,Cloneable,Serializable介面,這三個介面都是空介面,裡面沒有任何方法宣告。

 

RandomAccess是一個標記介面,其主要就是提供給List介面用的,用來表明其支援快速隨機訪問。因為這個介面是沒有任何實現的,實現了這個介面的類,就表明這個類支援快速訪問,就相當於實現了Serializable就等於支援序列化和反序列化,這是個標準。

   

Cloneable介面,表明這個是可以進行淺拷貝的,是可以呼叫Object.clone()返回該物件的淺拷貝。
  什麼是淺拷貝呢?
  舉個例子:
  假設x是一個飛空物件,應該有:
  (x.clone()!=x )==true,也就是說它們不是同一個物件。
  (x.clone().getClass()==x.getClass())==true,也就是它們是同一個型別的class,
  (x.equals(x.clone()))==true,也就是,邏輯上和內容上,它們應該是相同的。
  實現了Cloneable的類應該要滿足上面的幾個情況。

   

Serializable介面是我們經常遇到的介面,表明該類支援序列化和反序列化操作。

所以ArrayList繼承這三個沒有任何方法定義的介面只是為了表明這個類是支援隨機快速訪問的,可以支援淺拷貝的,可以被序列化和反序列化的。

都說ArrayList是動態陣列,那麼它裡面儲存資料的資料結構是什麼呢?

private transient Object[] elementData;  
   
  • 1

上面這個物件陣列就是其儲存元素的資料結構,前面有一個java關鍵字transient,這個關鍵字是去序列化的意思,即,在這個類序列化後儲存到磁碟或者輸出到輸出流的時候,這個物件陣列是不被儲存或者輸出的。

這裡又有疑問了,這個陣列不是儲存我們儲存的資料的嗎?為什麼要去序列化呢?那麼如果去掉序列化之後,我們儲存的元素從哪裡來呢?

這就跟這個ArrayList的特性有關,我們知道ArrayList的容量,也就是這個陣列的容量,一般都是預留一些容量,等到容量不夠時再拓展,那麼就會出現容量還有冗餘的情況,如果這時候進行序列化,整個陣列都會被序列化,連後面沒有意義空元素的也被序列化。這些是不應該被儲存的。所以java的設計者,就為這個類提供了一個writeObject方法,在實現了Serializable介面的類,如果這個類提供了writeObject方法,那麼在進行序列化的時候就會通過writeObject方法進行序列化,所以ArrayList的writeObject方法就會顯式的為每個實際的陣列元素進行序列化,只序列化有用的元素

private void writeObject(java.io.ObjectOutputStream s)          throws java.io.IOException{          // Write out element count, and any hidden stuff          int expectedModCount = modCount;          s.defaultWriteObject();          // Write out array length          s.writeInt(elementData.length);          // Write out all elements in the proper order.          for (int i=0; i<size; i++)              s.writeObject(elementData[i]);          if (modCount != expectedModCount) {              throw new ConcurrentModificationException();          }      }  
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

如果想了解更多有關java序列化的知識,請擢:http://zhh9106.iteye.com/blog/2152397

ArrayList的構造方法:

/**      * Constructs an empty list with the specified initial capacity.      *      * @param  initialCapacity  the initial capacity of the list      * @throws IllegalArgumentException if the specified initial capacity      *         is negative      */      public ArrayList(int initialCapacity) {          super();          if (initialCapacity < 0)              throw new IllegalArgumentException("Illegal Capacity: "+                                                 initialCapacity);          this.elementData = new Object[initialCapacity];      }      /**      * Constructs an empty list with an initial capacity of ten.      */      public ArrayList() {          this(10);      }      /**      * Constructs a list containing the elements of the specified      * collection, in the order they are returned by the collection's      * iterator.      *      * @param c the collection whose elements are to be placed into this list      * @throws NullPointerException if the specified collection is null      */      public ArrayList(Collection<? extends E> c) {          elementData = c.toArray();          size = elementData.length;          // c.toArray might (incorrectly) not return Object[] (see 6260652)          if (elementData.getClass() != Object[].class)              elementData = Arrays.copyOf(elementData, size, Object[].class);      } 
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

ArrayList提供了三個構造方法,第一個是由呼叫者傳入指定List的大小來建立elementData陣列。第二個是預設的構造方法,預設陣列容量是10。第三個是根據傳入的一個集合,將集合轉化成陣列,然後賦給elementData。

下面看一些ArrayList裡面需要注意的方法。

clone()

/**      * Returns a shallow copy of this <tt>ArrayList</tt> instance.  (The      * elements themselves are not copied.)      *      * @return a clone of this <tt>ArrayList</tt> instance      */      public Object clone() {          try {              @SuppressWarnings("unchecked")                  ArrayList<E> v = (ArrayList<E>) super.clone();              v.elementData = Arrays.copyOf(elementData, size);              v.modCount = 0;              return v;          } catch (CloneNotSupportedException e) {              // this shouldn't happen, since we are Cloneable              throw new InternalError();          }      } 
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

上面提到,ArrayList實現了Cloneable介面,支援返回淺拷貝,這裡是重寫了Object類的clone方法,返回的是一個副本(物件例項不同,內容相同,因為物件例項都不同,所以modCount也設為0)

remove(int index)

    /**      * Removes the element at the specified position in this list.      * Shifts any subsequent elements to the left (subtracts one from their      * indices).      *      * @param index the index of the element to be removed      * @return the element that was removed from the list      * @throws IndexOutOfBoundsException {@inheritDoc}      */      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);          elementData[--size] = null; // Let gc do its work          return oldValue;      }  
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

根據傳入的index,然後利用System.arraycopy()方法將index後面的元素從index位置開始進行覆蓋,這裡需要注意的是,index+1到size位置的元素,覆蓋掉index到size位置的元素,所以最後的兩個元素肯定相同,即重複了,所以最後一步會有elementData[–size] = null;將最後一個元素置為null。最後返回刪除元素的值。

remove(Object o)與fastRemove(int index),這兩個方法時一起使用的

/**      * Removes the first occurrence of the specified element from this list,      * if it is present.  If the list does not contain the element, it is      * unchanged.  More formally, removes the element with the lowest index      * <tt>i</tt> such that      * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>      * (if such an element exists).  Returns <tt>true</tt> if this list      * contained the specified element (or equivalently, if this list      * changed as a result of the call).      *      * @param o element to be removed from this list, if present      * @return <tt>true</tt> if this list contained the specified element      */      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 remove method that skips bounds checking and does not      * return the value removed.      */      private void fastRemove(int index) {          modCount++;          int numMoved = size - index - 1;          if (numMoved > 0)              System.arraycopy(elementData, index+1, elementData, index,                               numMoved);          elementData[--size] = null; // Let gc do its work      }  
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

這個是根據傳入的元素來移除,首先找到要刪除元素的位置,然後呼叫fastRemove()方法進行移除,這裡大家會有疑問,既然找到index,為什麼不用上門那個移除方法處理呢?非要寫個fastRemove(),看程式碼就知道,fastRemove跳過了index越界處理,並且不用返回要刪除的元素值。

clear()

/**      * Removes all of the elements from this list.  The list will      * be empty after this call returns.      */      public void clear() {          modCount++;          // Let gc do its work          for (int i = 0; i < size; i++)              elementData[i] = null;          size = 0;      }  
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

clear方法並不是把整個陣列都刪除,因為畢竟已經申請了記憶體,這樣刪了,很可惜,因為可能以後還用得著,這就免去了再次去申請記憶體的麻煩。這裡的clear只是把每個元素的都置為null,並把size設為0.

add(int index, E element)

/**      * Inserts the specified element at the specified position in this      * list. Shifts the element currently at that position (if any) and      * any subsequent elements to the right (adds one to their indices).      *      * @param index index at which the specified element is to be inserted      * @param element element to be inserted      * @throws IndexOutOfBoundsException {@inheritDoc}      */      public void add(int index, E element) {          rangeCheckForAdd(index);          ensureCapacityInternal(size + 1);  // Increments modCount!!          System.arraycopy(elementData, index, elementData, index + 1,                           size - index);          elementData[index] = element;          size++;      }  
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

根據指定位置,插入指定元素,先進行越界檢查,再進行容量檢查,然後將從index開始的元素到最後的元素,從index+1位置開始往後複製,然後將指定元素插入到index位置,然後將容量size增加1。

addAll(Collection< ? extends E> c)

/**      * Appends all of the elements in the specified collection to the end of      * this list, in the order that they are returned by the      * specified collection's Iterator.  The behavior of this operation is      * undefined if the specified collection is modified while the operation      * is in progress.  (This implies that the behavior of this call is      * undefined if the specified collection is this list, and this      * list is nonempty.)      *      * @param c collection containing elements to be added to this list      * @return <tt>true</tt> if this list changed as a result of the call      * @throws NullPointerException if the specified collection is null      */      public boolean addAll(Collection<? extends E> c) {          Object[] a = c.toArray();          int numNew = a.length;          ensureCapacityInternal(size + numNew);  // Increments modCount          System.arraycopy(a, 0, elementData, size, numNew);          size += numNew;          return numNew != 0;      }  
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

在陣列後面插入一個指定集合裡面的所有元素,首先將集合轉化為陣列,然後進行容量檢查,將目標陣列元素拷貝到elementData上。這裡有一點需要注意,如果傳入的集合為null,那麼結果會返回FALSE。

addAll(int index, Collection< ? extends E> c)

/**     * Inserts all of the elements in the specified collection into this     * list, starting at the specified position.  Shifts the element     * currently at that position (if any) and any subsequent elements to     * the right (increases their indices).  The new elements will appear     * in the list in the order that they are returned by the     * specified collection's iterator.     *     * @param index index at which to insert the first element from the     *              specified collection     * @param c collection containing elements to be added to this list     * @return <tt>true</tt> if this list changed as a result of the call     * @throws IndexOutOfBoundsException {@inheritDoc}     * @throws NullPointerException if the specified collection is null     */     public boolean addAll(int index, Collection<? extends E> c) {         rangeCheckForAdd(index);         Object[] a = c.toArray();         int numNew = a.length;         ensureCapacityInternal(size + numNew);  // Increments modCount         int numMoved = size - index;         if (numMoved > 0)             System.arraycopy(elementData, index, elementData, index + numNew,                              numMoved);         System.arraycopy(a, 0, elementData, index, numNew);         size += numNew;         return numNew != 0;     }  
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

將集合元素根據指定的位置開始插入,這個方法跟上面方法的區別是,要插入到指定位置,那麼從原始碼可以看到,首先其會從原陣列index開始到最後的元素,從index+numNew開始往後複製,目的是空出numNew個位置來儲存目標集合的元素。然後再講目標陣列從index位置開始依次插入到原陣列。

從上面的插入的幾個方法會發現一個共同點,就是每次插入前都會進行容量檢查,檢查是否超出了容量,如果超出了,則進行擴容。那看看,它是如果進行擴容的?而這裡也是jdk1.6和jdk1.7的實現區別

ensureCapacityInternal(int minCapacity)

private void ensureCapacityInternal(int minCapacity) {          modCount++;          // overflow-conscious code          if (minCapacity - elementData.length > 0)              grow(minCapacity);      }  
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

根據傳入的最小需要容量minCapacity來和陣列的容量長度對比,若minCapactity大於或等於陣列容量,則需要進行擴容,呼叫grow()

grow()

/**      * Increases the capacity to ensure that it can hold at least the      * number of elements specified by the minimum capacity argument.      *      * @param minCapacity the desired minimum capacity      */      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);      }  
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

首先得到陣列的舊容量,然後進行oldCapacity + (oldCapacity >> 1),將oldCapacity 右移一位,其效果相當於oldCapacity /2,我們知道位運算的速度遠遠快於整除運算,整句運算式的結果就是將新容量更新為舊容量的1.5倍,然後檢查新容量是否大於最小需要容量,若還是小於最小需要容量,那麼就把最小需要容量當作陣列的新容量,接著,再檢查新容量是否超出了ArrayList所定義的最大容量,若超出了,則呼叫hugeCapacity()來比較minCapacity和MAX_ARRAY_SIZE,如果minCapacity大於最大容量,則新容量則為ArrayList定義的最大容量,否則,新容量大小則為minCapacity。還有一點需要注意的是,容量拓展,是建立一個新的陣列,然後將舊陣列上的陣列copy到新陣列,這是一個很大的消耗,所以在我們使用ArrayList時,最好能預計資料的大小,在第一次建立時就申請夠記憶體。

下面附上hugeCapacity()程式碼:

private static int hugeCapacity(int minCapacity) {          if (minCapacity < 0) // overflow              throw new OutOfMemoryError();          return (minCapacity > MAX_ARRAY_SIZE) ?              Integer.MAX_VALUE :              MAX_ARRAY_SIZE;      }  
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

以上介紹了ArrayList幾個需要注意的方法原始碼實現原理,上面我們提到jdk1.6和jdk1.7在ArrayList上的實現區別就在於陣列的容量擴充套件,以上程式碼都是jdk1.7的,下面來看看jdk1.6的容量擴充套件的實現:

/**        * Increases the capacity of this <tt>ArrayList</tt> instance, if        * necessary, to ensure that it can hold at least the number of elements        * specified by the minimum capacity argument. &n