1. 程式人生 > >java集合核心原始碼01——ArrayList

java集合核心原始碼01——ArrayList

首先呢,對於ArrayList,相當於一個動態陣列,裡面可以儲存重複資料,並且支援隨機訪問,不是執行緒安全的。對於更多的底層東西,且聽分解。

開啟原始碼,先看繼承了哪些類,實現了哪些介面,然後繼承的這些類或介面是否還有父類,一直深挖到頂部

 

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

可以看出,ArrayList集合繼承了AbstractList類,實現了List,RandomAccess,Cloneable,Serializable這一系列介面,一個一個分析。

 

其中呢AbstractList類最終也是實現了Collection介面,List也是繼承了Collection介面,同時呢Collection介面繼承了Iterable介面。

對於Iterable介面呢,原始碼中解釋說,for-each迴圈可以與任何實現了Iterable介面的物件一起工作,就是說該集合可以使用for-each迴圈。

對於Cloneable,Serializable這兩個介面,代表該類支援克隆和序列化。

對於RandomAccess,實現了該介面,那麼該類就支援快速的隨機訪問。LinkedList就沒有實現該介面,需要用迭代器進行遍歷。

對於AbstractList類,裡面有一個屬性modCount,這個屬性主要是由集合的迭代器使用的,在迭代的過程中,會檢查當前的List的modCount和自己儲存的比較,是否發生了變化(變化是指該集合是否被其他執行緒併發修改),如果發生變化,就丟擲java.util.ConcurrentModificationException異常,這種行為就是fail-fast機制

 

接下來,我們真正的走進ArrayList原始碼:

 transient Object[] elementData;

首先,ArrayList底層也是封裝的一個數組,這個陣列是被transient關鍵字修飾的,代表物件的臨時資料,持久化物件時,不需要維持,所以不需要參與序列化,即用該關鍵字修飾。(該關鍵字修飾的例項變數不參與序列化)

然後呢,看一下ArrayList有哪些構造器:

  private static final int DEFAULT_CAPACITY = 10;//預設容量大小為10
  private int size;//提供私有變數儲存陣列的長度
  /**
  *接收一個int型的整數,初始化陣列容量   */   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型別的集合,把集合的元素複製到陣列中 * */ public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { this.elementData = EMPTY_ELEMENTDATA; } }

 

另外呢講一下ArrayList的擴容:

ensureCapacity()方法是用來首先計算出一個最小擴充套件的容量minExpand,然後所需要的最小容量和最小擴充套件容量相比較,如果大於,就會進入ensureExplicitCapacity方法,也是進行判斷後,然後進入主要的方法grow方法,計算新的容量大小為舊容量+舊容量/2(右移一位相當於除以2)即為原來長度的1.5倍,為什麼是1.5倍呢?因為由實驗可得出,1.5倍不會浪費太大的記憶體。

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

public
void ensureCapacity(int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } private void ensureExplicitCapacity(int minCapacity) { modCount++; if (minCapacity - elementData.length > 0) grow(minCapacity); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); 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; }

 

除此之外呢,還有一個方法,這個方法是來判斷陣列中元素個數和陣列長度的,來進行適時的釋放記憶體空間,適時的呼叫此方法減少記憶體佔用:

 public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

 

從原始碼中可以看出,ArrayList沒有實現任何的同步,所以不是執行緒安全的,其他的新增,刪除方法可以自行點開原始碼檢視,相應的很簡單。