一、概述
ArrayList是開發時經常使用到的集合類,它是用數組實作的,具有通路快,增删慢的特點。本文主要對ArrayList的常用方法進行分析。
二、變量
/**
* 預設的初始容量大小
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 容量為0時,elementData的值
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 使用無參構造函數時,elementData的預設值
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 存放元素的數組
*/
transient Object[] elementData;
/**
* 元素數量
*/
private int size;
三、構造函數
ArrayList中有三個構造函數,這三個構造函數都是public修飾的,是以有3種方式建立ArrayList對象。
無參構造函數
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
參數為整型變量
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);
}
}
這裡的邏輯比較簡單,主要是校驗參數,然後為ArrayList設定初始容量,将elementData 初始化為指定長度的數組。initialCapacity等于0的時候,将elementData設定為EMPTY_ELEMENTDATA,initialCapacity小于0時抛出不支援的參數異常。
參數為Collection<? extends E>類型變量
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// 6260652是jdk的bug編号
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
此構造函數構造一個包含指定集合的元素的清單。需要注意的是c.toArray方法,在注釋中說,c.toArray不一定會傳回Oject類型的數組,使用Arrays.asList(T… a)構件一個List時,傳回的對象是Arrays的靜态内部類Arrays.ArrayList,它存放元素不是用的Object數組 。是以需要校驗數組的類型,這是一個jdk中的bug,bug編号是6260652,可以到Oracle的官方網站中檢視關于這個bug的資訊https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6260652
四、主要方法
add(E e)方法
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
//保證elementData的大小能夠存放新元素
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
add()方法執行邏輯為:
1.保證elementData的大小能夠存放新元素,如果elementData數組放滿時,就會進行elementData的擴容。
2.将新元素存放到elementData數組中
進入ensureCapacityInternal方法
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
方法中的判斷elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA就是使用預設構造函數初始化ArrayList時,将minCapacity 設定為預設初始化大小10,也就是将elementData數組的大小設定為10。
進入ensureExplicitCapacity方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
modCount是modifyCount的簡寫,記錄修改次數,将修改次數+1,elementData數組放滿時,執行grow(minCapacity)進行擴容操作。
進入grow方法
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
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);
}
grow方法的邏輯為:
1.将容量大小擴容為原大小的1.5倍。
2.如果newCapacity不滿足存放元素的最小容量時,将newCapacity 設定為minCapacity。
3.如果newCapacity大于MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8)時,通過hugeCapacity(minCapacity)方法擴容。
4.将elementData中的元素複制到新數組中。完成擴容
add(int index, E element)方法
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++;
}
此方法的邏輯為:先對index參數進行檢查,然後進行進入ensureCapacityInternal方法,看是否進行擴容,然後将elemenData數組index位置以及index之後的所有元素都向後移動一個位置,讓index位置為null,然後将element放到index位置。
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
System.arraycopy方法是一個native方法,是由c語言實作。src是原數組對象,srcPos是原數組對象的開始位置,dest是目标對象,destPos是目标對象的開始位置,length是複制的長度。此方法的作用是将指定數組src從指定位置srcPos複制到目标數組dest的指定位置destPos。 複制的元件數量等于length參數。即将源數組src中srcPos至srcPos+length-1的對象分别複制到目标數組dest的destPos至destPos+length-1 位置。
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; // clear to let GC do its work
return oldValue;
}
主要邏輯為:
1、檢查index參數
2、修改記錄+1
3、擷取到index位置的對象
4、将index位置之後的所有對象向前移動一個位置
5、将最後一個位置的對象設定為null以便能夠被垃圾回收器清理