java集合框架-ArrayList

  雖然在源碼實現上jdk1.7和jdk1.8有所不同,但是ArrayList底層維護的依然是一個動態數組,每個ArrayList實例都有一個容量。該容量是指用來存儲列表元素的數組的大小。它總是至少等於列表的大小。隨着向 ArrayList 中不斷添加元素,其容量也自動增長。

      ArrayList不是同步的(也就是說不是線程安全的),如果多個線程同時訪問一個ArrayList實例,而其中至少一個線程從結構上修改了列表,那麼它必須保持外部同步,在多線程環境下,可以使用Collections.synchronizedList方法聲明一個線程安全的ArrayList,但是這種方法並不一定是安全的(比較複雜的一種情況),所以一般不用,例如:

      List arraylist = Collections.synchronizedList(new ArrayList());

      下面通過ArrayList的源碼來分析其原理,基於jdk1.8。

      1、ArrayList的構造方法:ArrayList提供了三種不同的構造方法

      1) ArrayList(),將elementData指向一個空對象數組,事實上在add方法的時候會做第一次初始化容量爲10

      2) ArrayList(int initialCapacity),構造一個具有指定初始容量的空列表。

      3) ArrayList(Collection<? extends E> c),構造一個包含指定 collection 的元素的列表,這些元素是按照該 collection 的迭代器返回它們的順序排列的。

      源碼如下:

  

1
private transient Object[] elementData;
private static final Object DEFAULTCAPACITY_EMPTY_ELEMENTDATA[] = new Object[0];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public ArrayList(int initialCapacity) {
     if(i>0)
elementData = new Object[i]; //初始化數組
     else if(i == 0)
            elementData = EMPTY_ELEMENTDATA;
          else
            throw new IllegalArgumentException((new StringBuilder()).append("Illegal Capacity: ").append(i).toString());
}
  public ArrayList() {
   elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
  }
  public ArrayList(Collection<? extends E> c) {
       elementData = c.toArray();   //返回包含此 collection 中所有元素的數組
   size = elementData.length;
   // c.toArray might (incorrectly) not return Object[] (see 6260652)
   if (elementData.getClass() != Object[].class)
      elementData = Arrays.copyOf(elementData, size, Object[].class); //複製指定的數組,返回包含相同元素和長度的Object類型的數組
  }

  當採用不帶參數的構造方法ArrayList()生成一個集合對象時,其實是在底層調用ArrayList(int initialCapacity)這一構造方法生產一個長度爲10的Object類型的數組。當採用帶有集合類型參數的構造方法時,在底層生成一個包含相同的元素和長度的Object類型的數組。

 

     2、add方法:ArrayList提供了兩種添加元素的add方法

     1) add(E e),將指定的元素添加到此列表的尾部。

     2) add(int index, E e),將指定的元素插入此列表中的指定位置。向右移動當前位於該位置的元素(如果有)以及所有後續元素(將其索引加 1)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public boolean add(E e) {
    ensureCapacity(size + 1);  // 擴大數組容量
    elementData[size++] = e;   //將元素e添加到下標爲size的Object數組中,並且執行size++
    return true;
    }
public void add(int index, E element) {
    if (index > size || index < 0)  //如果指定要插入的數組下標超過數組容量或者指定的下標小於0,拋異常
        throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
 
    ensureCapacity(size+1);  // 擴大數組容量
    System.arraycopy(elementData, index, elementData, index + 1,size - index);  //從指定源數組中複製一個數組,複製從指定的位置開始,到目標數組的指定位置結束。<br>                                                                                    // elementData --- 源數組   index --- 源數組中的起始位置   <br>                                                                                    // elementData --- 目標數組  index+1 ---  目標數組中的起始位置<br>                                                                                    // size - index --- 要複製的數組元素的數量
    elementData[index] = element; //將要添加的元素放到指定的數組下標處
    size++;
    }
1
2
3
4
5
6
7
8
9
10
11
12
public void ensureCapacity(int i) {
   byte byte0 = ((byte)(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA?10:0))//如果是第一次add,則容量爲10
  if(i > byte0)
ensureExplicitCapacity(i);
}
private void ensureExplicitCapacity(int i){
 modCount++;
   int oldCapacity = elementData.length;  //原數組的容量
   if (i > oldCapacity) {
       Object oldData[] = elementData;
       int newCapacity = (oldCapacity * 3)/2 1;  //定義新數組的容量,爲原數組容量的1.5倍+1
           if (newCapacity < minCapacity)
       newCapacity = minCapacity;
           // minCapacity is usually close to size, so this is a win:
           elementData = Arrays.copyOf(elementData, newCapacity);  //複製指定的數組,返回新數組的容量爲newCapacity
   }
   }

  如果集合中添加的元素超過了10個,那麼ArrayList底層會新生成一個數組,長度爲原數組的1.5倍+1,並將原數組中的元素copy到新數組中,並且後續添加的元素都會放在新數組中,當新數組的長度無法容納新添加的元素時,重複該過程。這就是集合添加元素的實現原理。

 

     3、get方法:

     1) get(int index),返回此列表中指定位置上的元素。

     

1
2
3
4
5
6
7
8
9
10
11
public E get(int index) {
    RangeCheck(index); //檢查傳入的指定下標是否合法
 
    return (E) elementData[index];  //返回數組下標爲index的數組元素
    }
 
private void RangeCheck(int index) {
    if (index >= size)  //如果傳入的下標大於或等於集合的容量,拋異常
        throw new IndexOutOfBoundsException(
        "Index: "+index+", Size: "+size);
    }

  

     4、remove方法:

     1) E remove(int index),移除此列表中指定位置上的元素。向左移動所有後續元素(將其索引減 1)。

     2) boolean remove(Object o),移除此列表中首次出現的指定元素(如果存在)。如果列表不包含此元素,則列表不做改動,返回boolean值。

     

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
public E remove(int index) {
    RangeCheck(index);  //檢查指定的下標是否合法
 
    modCount++;
    E oldValue = (E) 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;
    }
 
 public boolean remove(Object o) {
    if (o == null) { //如果傳入的參數爲null
            for (int index = 0; index < size; index++)
        if (elementData[index] == null) {  //移除首次出現的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) {  //移除指定位置的元素,實現方法類似remove(int i)
        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
    }

 

    5、clone方法:

    1) Object clone(),返回此ArrayList實例的淺表副本(不復制這些元素本身) 。

    

1
2
3
4
5
6
7
8
9
10
11
public Object clone() {
   try {
       ArrayList<E> v = (ArrayList<E>) super.clone();  //調用Object類的clone方法返回一個ArrayList對象
       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();
   }
   }

  

    以上通過對ArrayList部分關鍵源碼的分析,知道了ArrayList底層的實現原理,關於ArrayList源碼有以下幾點總結:

    1) ArrayList 底層是基於數組來實現的,可以通過下標準確的找到目標元素,因此查找的效率高;但是刪除元素會涉及到大量元素的位置移動,效率低。

    2) ArrayList提供了三種不同的構造方法,當集合中添加的元素個數大於10,數組會自動進行擴容,即生成一個新的數組,並將原數組的元素放到新數組中。

    3) ensureCapacity方法對數組進行擴容,它會生成一個新數組,長度是原數組的1.5倍+1,隨着向ArrayList中不斷添加元素,當數組長度無法滿足需要時,重複該過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章