一、首先看看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()