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

java集合核心源碼01——ArrayList

lar _array erro 進行 查看 機制 ans each循環 tex

首先呢,對於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沒有實現任何的同步,所以不是線程安全的,其他的添加,刪除方法可以自行點開源碼查看,相應的很簡單。

java集合核心源碼01——ArrayList