底層的數據結構就是數組,數組元素類型爲Object類型,便可以存放全部類型數據。
咱們對ArrayList類的實例的全部的操做底層都是基於數組的。java
ArrayList繼承的父類爲:AbstractList(抽象類)
實現的接口有:List(規定了List的操做規範)、RandomAccess(可隨機訪問)、Cloneable(可拷貝)、Serializable(可序列化)數組
友情提示:由於ArrayList實現了RandomAccess接口,因此儘可能用for(int i = 0; i < size; i++) 來遍歷而不要用Iterator迭代器來遍歷,後者在效率上要差一些數據結構
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
類的屬性中核心的屬性爲elementData,類型爲Object[],用於存放實際元素,而且被標記爲transient,也就意味着在序列化的時候,此字段是不會被序列化的。dom
友情提示:ArrayList的默認容量爲10函數
// 缺省容量 private static final int DEFAULT_CAPACITY = 10; // 元素數組(調用指定初始值的構造函數時elementData的長度會變成指定值) transient Object[] elementData; // 空對象數組 private static final Object[] EMPTY_ELEMENTDATA = {}; // 缺省空對象數組 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
友情提示:建立ArrayList時儘可能設置初始大小(使用ArrayList(int initialCapacity)構造函數)源碼分析
/** * ArrayList帶容量大小的構造函數 * * @param initialCapacity */ //說明:指定elementData數組的大小,不容許初始化大小小於0,不然拋出異常。 public ArrayList(int initialCapacity) { if (initialCapacity > 0) {// 初始容量大於0 this.elementData = new Object[initialCapacity];// 初始化元素數組(新建一個數組) } else if (initialCapacity == 0) {// 初始容量爲0 this.elementData = EMPTY_ELEMENTDATA;// 爲空對象數組 } else {// 初始容量小於0,拋出異常 throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity); } } /** * ArrayList無參構造函數。默認容量是10 */ //說明:當未指定初始化大小時,會給elementData賦值爲空集合。 public ArrayList() { // 無參構造函數,設置元素數組爲空 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
友情提示:
add方法:當容量到達size時進行擴容(add(E e)中先調用了ensureCapacity(size+1)方法,以後將元素的索引賦給elementData[size],然後size自增),擴容爲當前容量的0.5倍(若果ArrayList的當前容量爲10,那麼一次擴容後的容量爲15)
set方法:調用set方法時會檢驗索引是否合法(只校驗了上限)(index不能等於size(index<size))
get方法:調用get方法時也會檢驗索引是否合法(只校驗了上限)(index不能等於size(index<size))
remove方法:在移除指定下標的元素時,會把指定下標到數組末尾的元素向前移動一個單位,而且會把數組最後一個元素設置爲null,這樣是爲了方便以後整個數組不被使用時,能夠被GC;元素移動時使用的是System.arraycopy()方法測試
/** * 添加元素 * * @param e * @return */ public boolean add(E e) { //確保elementData數組有合適的大小 ensureCapacityInternal(size + 1); elementData[size++] = e; return true; } /** * 設定指定下標索引的元素值 * * @param index * @param element * @return */ public E set(int index, E element) { // 檢驗索引是否合法 rangeCheck(index); // 舊值 E oldValue = elementData(index); // 賦新值 elementData[index] = element; // 返回舊值 return oldValue; } /** * 獲取指定下標的元素 * * @param index * @return */ //說明:get函數會檢查索引值是否合法(只檢查是否大於size,而沒有檢查是否小於0), // 值得注意的是,在get函數中存在element函數,element函數用於返回具體的元素 public E get(int index) { // 檢驗索引是否合法 rangeCheck(index); return elementData(index); } // Positional Access Operations /** * 位置訪問操做 * * @param index * @return */ //說明:返回的值都通過了向下轉型(Object -> E(泛型)) @SuppressWarnings("unchecked") E elementData(int index) { return (E) elementData[index]; } /** * 移除指定下標元素 * * @param index * @return */ //說明:remove函數用戶移除指定下標的元素 // 此時會把指定下標到數組末尾的元素向前移動一個單位,而且會把數組最後一個元素設置爲null,這樣是爲了方便以後將整個數組不被使用時,能夠會被GC。 //提醒:元素移動時使用的是System.arraycopy()方法 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); // 賦值爲空,有利於進行GC elementData[--size] = null; // clear to let GC do its work // 返回舊值 return oldValue; } /** * 在指定下標位置插入元素 * @param index * @param element */ public void add(int index, E element) { //檢查索引是否合法 rangeCheckForAdd(index); ensureCapacityInternal(size + 1); System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; }
友情提示:rangeCheckForAdd方法用於add(int index, E element)和addAll(int index, Collection<? extends E> c)方法中檢驗索引是否合法;rangeCheck方法用於get、set等方法中檢驗索引是否合法(由於不改變數據結構,故index不能取到size,最大隻能取到size-1)this
//檢驗索引是否合法(只校驗了上限)(index不能等於size(index<size)) private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } //檢驗索引是否合法(校驗了上限和下限)(index能夠等於size(0<=index<=size)) private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
// 肯定ArrarList的容量。 // 若ArrayList的容量不足以容納當前的所有元素,設置 新的容量=「(原始容量x3)/2 + 1」 public void ensureCapacity(int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY;//默認容量10 if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } //確保elementData數組有合適的大小 private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// 判斷元素數組是否爲空數組 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);// 取較大值 } //確保elemenData數組有合適的大小 ensureExplicitCapacity(minCapacity); } //確保elemenData數組有合適的大小 private void ensureExplicitCapacity(int minCapacity) { // 結構性修改加1 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)// 新容量小於參數指定容量,修改新容量 newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0)// 新容量大於最大容量 newCapacity = hugeCapacity(minCapacity);// 指定新容量 // 拷貝擴容 elementData = Arrays.copyOf(elementData, newCapacity); }
public class AddTest { @Test public void test1(){ //咱們能夠看到,在add方法以前開始elementData = {}; // 調用add方法時會繼續調用,直至grow,最後elementData的大小變爲10, // 以後再返回到add函數,把8放在elementData[0]中 List<Integer> lists = new ArrayList<Integer>(); lists.add(8); } @Test public void test2(){ //說明:咱們能夠知道,在調用add方法以前,elementData的大小已經爲6,以後再進行傳遞,不會進行擴容處理。 List<Integer> lists = new ArrayList<Integer>(6);//elementData.length=6 lists.add(8); } }
public class RangeCheckTest { @Test public void test() { List list = new ArrayList(); list.add(1); list.add(2); list.add(3); //該語句報ArrayIndexOutOfBoundsException異常是rangeCheck(index)引起的(index >= size) System.out.println(list.get(10)); //rangeCheck(index)方法只校驗上線,該語句報ArrayIndexOutOfBoundsException異常是elementData[index]引起的 System.out.println(list.get(-1)); list.remove(-1);//同上 Object[] a = new Object[]{1, 2, 3}; System.out.println(a[-1]); } }
public class IndexOfTest { @Test public void test(){ List list = new ArrayList(); list.add(null); list.add(2); list.add(2); list.add(null); System.out.println(list.indexOf(null));//0 System.out.println(list.indexOf(2));//1 System.out.println(list.indexOf(3));//-1 } }
public class ToArrayTest { @Test public void test() { List list = new ArrayList(10); list.add(1); list.add(2); list.add(3); list.add(4); Object[] array =list.toArray(); //調用Arrays.copyOf()-->調用System.arraycopy() } }