jdk 1.7 ArrayList 源碼分析

一、首先看看ArrayList 的基類有哪些java

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

能夠總結爲:dom

  ArrayList 繼承了AbstractList 函數

               實現了 List 、 RondomAcess 、 cloneable 、 serializable 對於通常的容器都會實現cloneable 用於克隆 實現serializable 實現序列化this

 

二、在ArrayList中定義的變量spa

主要有4個變量,在瞭解這些變量以前咱們須要知道一些基本信息,ArrayList 底層用的是數組實現的,初始的默認容量是10 ,code

private static final int DEFAULT_CAPACITY = 10;//初始的默認容量對象

private static final Object[] EMPTY_ELEMENTDATA = {}; //空數組繼承

private transient Object[] elementData;//ArrayList 中真正存數據的容器ci

private int size;//當前數組中的數據個數

 

三、ArrayList中的構造函數

ArrayList中總共有三個構造函數,分別是有初始化容量的構造函數,沒有參數的構造函數,帶有集合參數的構造函數

(1) 從源碼中咱們能夠看到這只是一個很簡單的 建立數組對象的過程,不過在建立對象以前須要對初始容量值判斷是否<0 

public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];  
}

(2)無參的構造函數,這裏咱們須要注意的是雖然數組默認容量是10 ,可是無參的狀況下數組的初始化實際是個空數組而不是建立大小爲10的數組
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}

(3)集合 由於是對數組操做因此 用的是Arrays.copyOf 進行復制和類型轉換
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)增長一個數的操做,size記錄當前數組中的數個數因此實際操做是在數組下標爲size位置賦值,而後 size++ ,可是在這以前得判斷size+1是否越界,也就是是否須要進行擴容,擴容咱們下面再講,該函數的返回值是Boolean類型

public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}

 

(2)在指定的下標增長一個數的操做。在這裏咱們須要注意一個函數就是System.arraycopy()這個是ArrayList中經常使用的API,主要用於數組移位

原先的數存在elementData[]中,一共size個對象,如今咱們須要在下標爲index插入新對象 就須要咱們將 下標[index ,size-1]範圍的對象移位到[index+1,size]

操做是   System.arraycopy(elementData, index, elementData, index + 1,size - index);

                                      (源數組 ,複製開始下標,目標數組,目標數組開始下標,複製對象的個數)     

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++;
}

(3)增長一個集合的操做。首先將集合轉換爲數組,再判斷假如該集合數目的對象是否須要擴容,再調用System.arraycopy函數將新數組中的數複製到elementData[]中

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)刪除下標爲index的對象,先判斷index是否有效,而後是向前移一位複製,最後size下標位置賦值爲null,返回刪除的對象

public E remove(int index) {
      rangeCheck(index);

      modCount++;
      E oldValue = elementData(index);//這裏可能會疑惑爲何是elementData(index)而不是elementData[index] 由於elementData(index)是ArrayList的一個內部函數,實際也是返回下標爲index的對象。

      int numMoved = size - index - 1;
      if (numMoved > 0)
      System.arraycopy(elementData, index+1, elementData, index,numMoved);// 又出現了System.arraycopy。。。。當別人問我ArrayList中對什麼印象最深入那麼就是這個了
      elementData[--size] = null; // clear to let GC do its work

     return oldValue;
}

(2)刪除某個對象操做。實際上這是個遍歷數組順便比較的過程,分兩個條件去遍歷,當刪除的對象是 null的狀況可非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;
}

 

(3) 刪除一個集合的操做。

public boolean removeAll(Collection<?> c) {
          return batchRemove(c, false);// 調用一個內部函數
}

//這個函數利用了tow poit 的思想,一個讀節點,一個寫節點,遍歷數組當該對象不存在集合中時保留下來,最後對[w,size]範圍賦值爲null

private boolean batchRemove(Collection<?> c, boolean complement) {
     final Object[] elementData = this.elementData;
     int r = 0, w = 0;
     boolean modified = false;
     try {
     for (; r < size; r++)
           if (c.contains(elementData[r]) == complement)
           elementData[w++] = elementData[r];
      } finally {
     // Preserve behavioral compatibility with AbstractCollection,
     // even if c.contains() throws.
     if (r != size) {
            System.arraycopy(elementData, r,elementData, w,size - r);
            w += size - r;
      }
     if (w != size) {
     // clear to let GC do its work
           for (int i = w; i < size; i++)
           elementData[i] = null;
           modCount += size - w;
           size = w;
           modified = true;
      }
      }
      return modified;
}

(1) 對index下標處的對象賦值新的對象,首先判斷index是否有限,而後對數組下標爲index位置賦值,返回舊對象

public E set(int index, E element) {
     rangeCheck(index);

     E oldValue = elementData(index);
     elementData[index] = element;
      return oldValue;
}

 

(1)查找下標爲index 的對象 ,先判斷 index是否有效,再返回下標爲index的數組

public E get(int index) {
      rangeCheck(index);

      return elementData(index);
}

擴容分析

上面咱們在分析增長一個對象操做以前會調用 ensureCapacityInternal(size + 1) ,如今咱們將沿着這條線去分析擴容的過程

private void ensureCapacityInternal(int minCapacity) {
       if (elementData == 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);
}

 //擴容函數

private void grow(int minCapacity) {
  // overflow-conscious code
      int oldCapacity = elementData.length;
      int newCapacity = oldCapacity + (oldCapacity >> 1); //這裏能夠看出擴容的容量是舊容量的1.5倍
      if (newCapacity - minCapacity < 0)//還要將1.5倍舊容量與最小須要的容量比較取較大值
              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);//建立新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; //從這裏能夠看出數組可以容許的最大擴容大小爲Integer.MAX_VALUE
}

 

其餘經常使用的函數

一、判空函數

public boolean isEmpty() {
      return size == 0;
}

二、查當前ArrayList中的對象個數

public int size() {
      return size;
}

三、是否含有某個對象

public boolean contains(Object o) {
       return indexOf(o) >= 0;
}

//一樣是分爲null 和非 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;
}

四、將容量縮小 , 減小空間

public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
          elementData = Arrays.copyOf(elementData, size); //建立size大小的新數組,將舊數組的值賦值到新數組
    }
}

 

總結: ArrayList 底層用的是數組實現,默認初始化容量爲10 ,擴容規則爲舊數組長度的1.5倍與當前須要的最小容量的最大值,最經常使用的操做是Arrays.copyOf() 和  System.arraycopy() 

相關文章
相關標籤/搜索