ArrayList源碼剖析

一、ArrayList的底層實現是一個數組,數組裏元素類型是Object類型,可以存放所有類型的數據,對ArrayList類的實例的所有操作都是基於數組的操作

ArrayList的數據結構如下:

  

二、ArrayList源碼分析

2.1 類的繼承關係

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
注:ArrayList繼承AbstractList抽象父類,實現了List接口(規範了List的操作規範),實現了RandomAccess接口(可隨機訪問),

實現了Cloneable(可拷貝),實現了Serializable接口(可序列化)。

2.2 類的屬性

    //版本號
    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; // non-private to simplify nested class access
    //實際元素大小,對象創建時會初始化爲0
    private int size;
    //最大數組容量
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
注:核心屬性爲elementDate數組,類型爲Object[],用於存放實際元素,並且不會被序列化。

2.3 類的構造函數

1. ArrayList(int)型構造函數

public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {//初始容量大於0
            this.elementData = new Object[initialCapacity];//初始化元素數組,大小爲初始容量
        } else if (initialCapacity == 0) {//初始容量小於0
            this.elementData = EMPTY_ELEMENTDATA;//初始化一個空數組
        } else {//初始容量小於0時,拋出異常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
2. ArrayList()型構造函數

public ArrayList() {
    	//無參構造函數,默認數組元素爲空
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
注:當未指定初始化大小時,給elementData賦值爲空數組集合。

3. ArrayList(Collection<? extends E>)型構造函數 

public ArrayList(Collection<? extends E> c) { //參數爲一個集合
        elementData = c.toArray();//將集合轉化爲數組
        if ((size = elementData.length) != 0) {//參數傳遞過來的集合是非空的
            if (elementData.getClass() != Object[].class) //判斷是否轉化爲Object數組
            	//如果沒有轉化成功,就將集合裏的元素複製到一個Object數組裏
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else { //當集合爲空時,初始化一個空數組
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
注:當傳遞的參數爲集合類型時,會將集合 類型轉化爲數組類型,並賦值給elementDate。

2.4類的縮小容量機制

//當數組容量大小大於實際元素個數時,可以調用此方法節約空間
	public void trimToSize() {
        modCount++;
        //size爲實際元素個數
        //elementData.length爲數組容量
        if (size < elementData.length) {
        	//證明數組中有空元素,進行縮小容量操作
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

2.5 增加元素操作

1.外部調用的接口(返回增加成功或失敗)

public boolean add(E e) { //添加一個值,首先會確保容量
        ensureCapacityInternal(size + 1); 
        elementData[size++] = e;
        return true;
    }

上面是添加一個值,一般添加在數組的末尾,下面這段是在index位置添加一個元素

public void add(int index, E element) {
        rangeCheckForAdd(index); //檢查如果index越界或小於0,拋出異常
        ensureCapacityInternal(size + 1);  // 確保容量,保證不會溢出
        //public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 
        //src:源數組; srcPos:源數組要複製的起始位置; dest:目的數組; destPos:目的數組放置的起始位置; length:複製的長度
        System.arraycopy(elementData, index, elementData, index + 1, size - index); 
        elementData[index] = element;//將當前元素放在index數組上 size++; 
     }
2. add 方法裏面調用了ensureCapacityInternal(size + 1)方法

//在添加元素時,確保數組的容量
private void ensureCapacityInternal(int minCapacity) {
//當elementData爲空數組時,證明是第一次添加元素
//將參數和默認初始容量中的較大值設爲數組容量
     if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
         minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
      }
     //不是第一次添加時,當參數大於數組容量時,增加數組容量
     ensureExplicitCapacity(minCapacity);
}
3.上述ensureCapacityInternal方法中又調用了ensureExplicitCapacity(minCapacity)

private void ensureExplicitCapacity(int minCapacity) {
	modCount++;
    //參數容量大於數組容量,就是數組不夠用了,調用grow()函數進行擴容
    if (minCapacity - elementData.length > 0)
       grow(minCapacity);
}
注:grow纔會對數組進行真正的擴容,ensureCapacityInternal、ensureExplicitCapacity都只是過程。
2.5 數組擴容 grow()源碼如下

private void grow(int minCapacity) {
	int oldCapacity = elementData.length;//舊容量
    int newCapacity = oldCapacity + (oldCapacity >> 1);//新容量爲舊容量的1.5倍
    //比較新容量和參數值,取出較大的值作爲將要擴容的容量
      if (newCapacity - minCapacity < 0)
         newCapacity = minCapacity;
      //如果將要擴容的容量大於最大可以允許的值,檢查是否會溢出,由hugeCapacity檢查
      //如果溢出,拋出異常,如果沒溢出,將要擴容的容量爲最大值
      if (newCapacity - MAX_ARRAY_SIZE > 0)
         newCapacity = hugeCapacity(minCapacity);
      //按照行容量進行擴容
      elementData = Arrays.copyOf(elementData, newCapacity);
}
注:正常情況下會擴容1.5倍,特殊情況下(新擴展數組大小已經達到了最大值)則只取最大值。
   當我們調用add方法時,實際上的函數調用如下
2.6 grow函數對hugeCapacity進行了調用
//將要擴容的容量大於最大值,調用此函數判斷是否會溢出
//溢出則拋異常,沒有溢出的話將要擴容的容量就爲最大值
private static int hugeCapacity(int minCapacity) {
	if (minCapacity < 0) // 溢出
	    throw new OutOfMemoryError(); //拋異常
	return (minCapacity > MAX_ARRAY_SIZE) ?
	    Integer.MAX_VALUE :
	    MAX_ARRAY_SIZE;
}
2.7 返回數組大小

public int size() {
	        return size;
	    }
2.8 判斷數組是否爲空

public boolean isEmpty() {
	        return size == 0;
	    }
2.9判斷數組是否包含此元素

public boolean contains(Object o) {
	        return indexOf(o) >= 0;
	    }
2.10 找出當前值在數組中首次出現的位置 indexOf(Object o)

//找出當前值在數組中首次出現的位置,會根據當前值是否爲null使用不同的方式去判斷
		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;
	    }
2.11 public int lastIndexOf(Object o) ,找出當前元素在數組中最後一次出現的位置

//找出當前值在數組中最後一次出現的位置,不存在就返回-1
		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;
	    }
2.11 克隆

//返回副本,元素本身沒有被賦值,如果數組中途發生改變就拋出異常
		 public Object clone() {
		        try {
		            ArrayList<?> v = (ArrayList<?>) 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(e);
		        }
		    }
2.12  toArray() 將數組轉換爲Object數組

//轉換爲Object數組,使用Arrays.copyOf方法(相當於複製)
		 public Object[] toArray() {
		        return Arrays.copyOf(elementData, size);
		    }
2.13 通過下標獲得數組元素的值

public E get(int index) {
	        rangeCheck(index); //對下標進行越界的檢測
	        return elementData(index);
	    }
2.14  在固定的下標上設置元素

public E set(int index, E element) {
	        rangeCheck(index);//對下標進行越界的檢測
	        E oldValue = elementData(index);//取出index位置上的值賦給oldValue
	        elementData[index] = element;//將當前值放到index位置上
	        return oldValue; //返回oldValue
	    }
2.15 public boolean remove(Object o)  刪除指定元素首次出現的位置,只返回是否刪除成功,根據元素是否爲null有兩種不同的方式

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;
	    }
注:藉助fastRemove(index) 函數刪除元素,之所以叫快速刪除,是因爲不需要檢查元素下標是否符合範圍,也不需要返回值,只是內部使用
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; 
	    }

2.17 public E remove(int index) 刪除指定位置的值,並且會檢查下標和返回被刪除的值

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; 
	        return oldValue;
	    }
2.18 public void clear() 清空數組,將每一個值設爲null,方便垃圾回收

public void clear() {
	        modCount++;
	        for (int i = 0; i < size; i++)
	            elementData[i] = null;
	     	 size = 0;
	    }
2.19 public boolean addAll(Collection<? extends E> c)  將集合的元素添加到末端,如果集合爲空返回false
public boolean addAll(Collection<? extends E> c) {
	        Object[] a = c.toArray();//將集合轉化爲Object類型的數組
	        int numNew = a.length;//數組的長度
	        ensureCapacityInternal(size + numNew);  //確保數組的容量
	        System.arraycopy(a, 0, elementData, size, numNew); //添加數據,實質是複製
	        size += numNew;
	        return numNew != 0;
	    }
2.20 public boolean addAll(int index, Collection<? extends E> c) 功能如上,只是從指定位置開始添加  
public boolean addAll(int index, Collection<? extends E> c) {
	        rangeCheckForAdd(index);
	        Object[] a = c.toArray();   //將集合轉化爲Object數組
	        int numNew = a.length;      // 數組長度
	        ensureCapacityInternal(size + numNew);  //確保容量
	        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;
	    }
2.21protected void removeRange(int fromIndex, int toIndex) 刪除一個範圍內的元素,從fromIndex到toIndex

protected void removeRange(int fromIndex, int toIndex) {
	        modCount++;
	        int numMoved = size - toIndex;
	        System.arraycopy(elementData, toIndex, elementData, fromIndex,
	                         numMoved);
	        int newSize = size - (toIndex-fromIndex);
	        for (int i = newSize; i < size; i++) {
	            elementData[i] = null;
	        }
	        size = newSize;
	    }
源碼我們就介紹到這裏,下面說一些總結性的東西
三、常見問題
3.1 爲什麼ArrayList不適合刪除和插入操作?
在ArrayList中在刪除和插入時會對數組進行移動,通過調用System.arrayCopy來複制數組,所以效率低下。
3.2 在ArrayList中提供了兩種迭代器Iterator和ListIterator,有什麼區別?
  

                     

從上圖可以看出,ListIterator相比Iterator來說,增加了增,刪,設定元素以及向前或向後遍歷元素的操作。
3.3快速失敗機制 fast-fail
容器的“快速失敗”機制是Java集合中的一種錯誤檢測的機制,當多個線程對集合進行結構上的改變的操作時,有可能會產生fail-fast機制。它是
一種迭代器檢測bug的機制,比如:我們有線程A和線程B,線程A通過Iterator訪問集合中的元素,某個時刻,線程B修改了集合中的結構,那麼程序
就會拋出ConcurrentModificationException。
實現:在add,remove,next中都調用了此方法,判斷modCount和expectedModCount是否相等來拋出異常

final void checkForComodification() {  
	            if (modCount != expectedModCount)  
	                throw new ConcurrentModificationException();  
	        }
在迭代器中expectedModCount 是這樣定義的,int expectedModCount = modCount; 這個值是不會變化的,所以使用迭代器遍歷整個ArrayList時,
我們只需要判斷那些可能會讓 ModCount 改變的方法,從而知道了集合是否發生了結構上的變化。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章