JDK1.8源碼閱讀ArrayList.class
ArrayList儲存在Object數組
- ArrayList數據是儲存在
Object[] elementData
數據內的。默認初始化是賦予10個元素空間 - ArrayList在序列化的時候會調用writeObject,直接將size和element寫入ObjectOutputStream;反序列化時調用readObject,從ObjectInputStream獲取size和element,再恢復到elementData。
- 爲什麼不直接用elementData來序列化,而採用上訴的方式來實現序列化呢?原因在於elementData是一個緩存數組,它通常會預留一些容量,等容量不足時再擴充容量,那麼有些空間可能就沒有實際存儲元素,採用上訴的方式來實現序列化時,就可以保證只序列化實際存儲的那些元素,而不是整個數組,從而節省空間和時間。(摘:https://blog.csdn.net/zero__007/article/details/52166306)
private static final int DEFAULT_CAPACITY = 10;//默認容量
private static final Object[] EMPTY_ELEMENTDATA = {};//空的對象數組
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//默認的空數組,無參構造函數創建的數組
transient Object[] elementData;//存放數據的數組的緩存變量,不可序列化
private int size;//元素數量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;//數組的最大容量可能會導致內存溢出
ArrayList繼承與實現
- ArrayList繼承於AbstractList,同時實現了List接口
- AbstractList繼承於AbstractCollection,同樣實現了List接口
- AbstractCollection實現Collection接口
- List接口繼承了Collection接口
- Collection接口繼承了Iterable接口(抱歉,你看到這段文字一定會很不舒服,只能說抱歉了)
//ArrayList.java
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
//AbstractList.java抽象類
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
//AbstractCollection.java抽象類
public abstract class AbstractCollection<E> implements Collection<E>
//List.java接口
public interface List<E> extends Collection<E>
//Collection.java接口
public interface Collection<E> extends Iterable<E>
//Iterable.java接口
public interface Iterable<T>
如何增加一個元素
默認增加一個元素不挑位置
- 先檢測空間是否足夠,然後直接在size+1位置放入新元素就可以了
/**
* 添加一個值,首先會確保容量
* @param e 要添加到此列表中的元素
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 擴容
elementData[size++] = e;// 將e賦值給elementData的size+1的位置
return true;
}
向指定位置添加一個元素
Java Native Interface,利用C++在底層實現
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos,int length);
- 判斷index是否越界
- 在插入位置往後移一個位,然後插入新元素
/**
* 在ArrayList的index位置,添加元素element,會檢查添加的位置和容量
* @param index 指定元素將被插入的索引
* @param element 要插入的元素
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
// 判斷index是否越界
rangeCheckForAdd(index);
// 擴容
ensureCapacityInternal(size + 1); // Increments modCount!!
//public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
//src:源數組; srcPos:源數組要複製的起始位置; dest:目的數組; destPos:目的數組放置的起始位置; length:複製的長度
// 將elementData從index位置開始,複製到elementData的index+1開始的連續空間
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 在elementData的index位置賦值element
elementData[index] = element;
// ArrayList的大小加一
size++;
}
如何刪除一個元素
按下標移除指定元素
- 對比還有個fastRemove快速刪除指定位置的值,不需要檢查和返回值,只允許內部使用
public E remove(int index) {
rangeCheck(index);// 判斷是否越界
modCount++;
E oldValue = elementData(index);// 讀取舊值
// 獲取index位置開始到最後一個位置的個數
int numMoved = size - index - 1;
if (numMoved > 0)
// 將elementData數組index+1位置開始拷貝到elementData從index開始的空間
System.arraycopy(elementData, index + 1, elementData, index, numMoved);
// 使size-1 ,設置elementData的size位置爲空,讓GC來清理內存空間
elementData[--size] = null; //便於垃圾回收器回收
return oldValue;
}
按元素刪除
- 原理同indexOf方法,遍歷時間複雜度O(n)
/**
* 在ArrayList的移除對象爲O的元素,跟indexOf方法思想基本一致
* @param o 要從該列表中刪除的元素(如果存在)
* @return true 如果這個列表包含指定的元素
*/
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;
}
private boolean batchRemove(Collection<?> c, boolean complement)
根據complement值,將ArrayList中包含c中元素的元素刪除或者保留public boolean removeAll(Collection<?> c)
ArrayList移除集合c中的所有元素
ArrayList其它內置方法
ArrayList(int initialCapacity)初始化容量
new一個該大小的object數組賦給
elementData
this.elementData = new Object[initialCapacity];
public ArrayList(int initialCapacity) {
// 如果初始化時ArrayList大小大於0
if (initialCapacity > 0) {
// new一個該大小的object數組賦給elementData
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {// 如果大小爲0
// 將空數組賦給elementData
this.elementData = EMPTY_ELEMENTDATA;
} else {// 小於0
// 則拋出IllegalArgumentException異常
throw new IllegalArgumentException("Illegal Capacity: " +initialCapacity);
}
}
//舉例:new ArrayList(88);
ArrayList(Collection<? extends E> c)構造一個指定的列表
構造一個包含指定c元素的列表,Arrays.copyOf(elementData, size, Object[].class)
/**
* 帶參數Collection的構造方法
* @param c 其元素將被放入此列表中的集合
* @throws NullPointerException 如果指定的集合是空的
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toarray可能(錯誤地)不返回對象[](見JAVA BUG編號6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA;// 使用空數組
}
}
trimToSize()縮小空間
/**
* 因爲容量常常會大於實際元素的數量。內存緊張時,可以調用該方法刪除預留的位置,調整容量爲元素實際數量。
* 如果確定不會再有元素添加進來時也可以調用該方法來節約空間
*/
public void trimToSize() {
modCount++;
// 如果size小於length
if (size < elementData.length) {
// 重新將elementData設置大小爲size
elementData = (size == 0)? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size);
}
}
grow(int minCapacity)擴容判斷(私有方法)
私有方法,每一次add的時候都調用該方法判斷擴容,elementData = Arrays.copyOf(elementData, newCapacity);先給1.5倍不夠再給minCapacity
private void grow(int minCapacity) {
// 獲取到ArrayList中elementData數組的內存空間長度
int oldCapacity = elementData.length;
// 擴容至原來的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 再判斷一下新數組的容量夠不夠,夠了就直接使用這個長度創建新數組,不夠就將數組長度設置爲需要的長度
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//若預設值大於默認的最大值檢查是否溢出
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 調用Arrays.copyOf方法將elementData數組指向新的內存空間時newCapacity的連續空間
// 並將elementData的數據複製到新的內存空間
elementData = Arrays.copyOf(elementData, newCapacity);
}
contains(Object o)是否包含某對象
調用indexOf判斷
/**
* 是否包含一個數 返回bool
* @param o 檢測o是否爲ArrayList中元素
* @return true 如果ArrayList中包含o元素
*/
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
/**
* 返回一個值在數組首次出現的位置,會根據是否爲null使用不同方式判斷。不存在就返回-1。時間複雜度爲O(N)
*/
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;
}
Object clone()克隆
/**
* 返回副本,元素本身沒有被複制,複製過程數組發生改變會拋出異常
* @return v ArrayList副本
*/
public Object clone() {
try {
// 調用父類(翻看源碼可見是Object類)的clone方法得到一個ArrayList副本
ArrayList<?> v = (ArrayList<?>) super.clone();
// 調用Arrays類的copyOf,將ArrayList的elementData數組賦值給副本的elementData數組
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
// 返回副本v
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}