public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io. Serializable
implementsjava
/** * 默認初始容量大小 */ private static final int DEFAULT_CAPACITY = 10; /** * 指定該ArrayList容量爲0時,返回該空數組。 */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 當調用無參構造方法,返回的是該數組。剛建立一個ArrayList 時,其內數據量爲0。 * 它與EMPTY_ELEMENTDATA的區別就是:該數組是默認返回的,然後者是在用戶指定容量爲0時返回。 */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * 保存ArrayList數據的數組 * 該值爲DEFAULTCAPACITY_EMPTY_ELEMENTDATA 時,當第一次添加元素進入ArrayList中時,數組將擴容值DEFAULT_CAPACITY。 */ transient Object[] elementData; /** * ArrayList 所包含的元素個數 */ private int size;
問:elementData被標記爲transient,那麼它的序列化和反序列化是如何實現的呢?
答:ArrayList自定義了它的序列化和反序列化方式。詳情請查看writeObject(java.io.ObjectOutputStream s)和readObject(java.io.ObjectOutputStream s)方法。git
ArrayList提供了三種構造方法。github
/** * 帶初始容量參數的構造函數。(用戶本身指定容量) */ public ArrayList(int initialCapacity) { //初始容量大於0 if (initialCapacity > 0) { //建立initialCapacity大小的數組 this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { //建立空數組 this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } /** * 默認構造函數,DEFAULTCAPACITY_EMPTY_ELEMENTDATA 爲0.初始化爲10, * 也就是說初始實際上是空數組 當添加第一個元素的時候數組容量才變成10 */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * 構造一個包含指定集合的元素的列表,按照它們由集合的迭代器返回的順序。 * 若是指定的集合爲null,throws NullPointerException。 */ public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); //若是指定集合元素個數不爲0 if ((size = elementData.length) != 0) { // c.toArray 可能返回的不是Object類型的數組因此加上下面的語句用於判斷, if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); //【2】Arrays包含用來操做數組(好比排序和搜索)的各類方法。 //copyOf(U[] original, int newLength, Class<? extends T[]> newType) // 複製指定的數組,截取或用 null 填充(若有必要),以使副本具備指定的長度。 } else { this.elementData = EMPTY_ELEMENTDATA; } }
步驟:segmentfault
/** * 返回此列表中指定位置的元素。 * * @param index 須要返回的元素的索引 * @return list中索引爲index的元素 * @throws IndexOutOfBoundsException 若是索引超出size */ public E get(int index) { //越界檢查 rangeCheck(index); return elementData(index); } /** * 檢查給定的索引是否在範圍內。 */ private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } /** * 返回IndexOutOfBoundsException細節信息 */ private String outOfBoundsMsg(int index) { return "Index: "+index+", Size: "+size; } /** * 返回索引爲index的元素 */ @SuppressWarnings("unchecked") E elementData(int index) { return (E) elementData[index]; }
步驟:數組
整個擴容過程:安全
首先去檢查一下數組的容量是否足夠多線程
不足夠:擴容dom
/** * 將指定的元素追加到此列表的末尾。 */ public boolean add(E e) { //確認list容量,若是不夠,容量加1。注意:只加1,保證資源不被浪費 ensureCapacityInternal(size + 1); //這裏看到ArrayList添加元素的實質就至關於爲數組賦值 elementData[size++] = e; return true; }
擴容ide
/** * ArrayList的擴容機制 * ArrayList的擴容機制提升了性能,若是每次只擴充一個, * 那麼頻繁的插入會致使頻繁的拷貝,下降性能,而ArrayList的擴容機制避免了這種狀況。 * 若有必要,增長此ArrayList實例的容量,以確保它至少能容納元素的數量 * @param minCapacity 所需的最小容量 */ public void ensureCapacity(int minCapacity) { // 若是elementData等於DEFAULTCAPACITY_EMPTY_ELEMENTDATA,最小擴容量爲DEFAULT_CAPACITY,不然爲0 int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } /** * 數組容量檢查,不夠時則進行擴容。 * * @param minCapacity 想要的最小容量 */ private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 若elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA, // 則取minCapacity爲DEFAULT_CAPACITY和參數minCapacity之間的最大值 return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } //獲得最小擴容量 private void ensureCapacityInternal(int minCapacity) { // 獲取默認的容量和傳入參數的較大值 ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } //判斷是否須要擴容 private void ensureExplicitCapacity(int minCapacity) { modCount++; // 確保指定的最小容量 > 數組緩衝區當前的長度 if (minCapacity - elementData.length > 0) //擴容 grow(minCapacity); } /** * 要分配的最大數組大小 * 爲何要減去8呢? * 由於某些VM會在數組中保留一些頭字,嘗試分配這個最大存儲容量,可能會致使array容量大於VM的limit,最終致使OutOfMemoryError。 */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /** * ArrayList擴容的核心方法 * * 第一次擴容,邏輯爲newCapacity = oldCapacity + (oldCapacity >> 1);即在原有的容量基礎上增長一半。 * 第一次擴容後,若是容量仍是小於minCapacity,就將容量擴充爲minCapacity。 */ private void grow(int minCapacity) { // oldCapacity爲舊容量,newCapacity爲新容量 int oldCapacity = elementData.length; //將oldCapacity 右移一位,其效果至關於oldCapacity /2, //咱們知道位運算的速度遠遠快於整除運算,整句運算式的結果就是將新容量更新爲舊容量的1.5倍, int newCapacity = oldCapacity + (oldCapacity >> 1); //而後檢查新容量是否大於最小須要容量,若仍是小於最小須要容量,那麼就把最小須要容量看成數組的新容量, if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //再檢查新容量是否超出了ArrayList所定義的最大容量, //若超出了,則調用hugeCapacity()來比較minCapacity和 MAX_ARRAY_SIZE, //若是minCapacity大於MAX_ARRAY_SIZE,則新容量則爲Interger.MAX_VALUE,不然,新容量大小則爲 MAX_ARRAY_SIZE。 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); } //比較minCapacity和 MAX_ARRAY_SIZE private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
看完了代碼,能夠對擴容方法總結以下:函數
步驟:
/** * 在此列表中的指定位置插入指定的元素。 * 先調用 rangeCheckForAdd 對index進行界限檢查;而後調用 ensureCapacityInternal 方法保證capacity足夠大; * 再將從index開始以後的全部成員後移一個位置;將element插入index位置;最後size加1。 */ public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); //arraycopy()這個實現數組之間複製的方法必定要看一下,下面就用到了arraycopy()方法實現數組本身複製本身 //實現讓index開始以後的全部元素後移一個位置: //elementData:源數組;index:源數組中的起始位置;elementData:目標數組;index + 1:目標數組中的起始位置; size - index:要複製的數組元素的數量; System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; }
add(int index, E e)須要先對元素進行移動,而後完成插入操做,也就意味着該方法有着線性的時間複雜度,即O(n)
步驟:
/** * 刪除該列表中指定位置的元素。 將任何後續元素移動到左側(從其索引中減去一個元素)。 */ public E remove(int index) { //檢查索引是否越界 rangeCheck(index); //結構性修改次數+1【1】 modCount++; E oldValue = elementData(index); // 刪除指定元素後,須要左移的元素個數 int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); // size減一,而後將索引爲size-1處的元素置爲null。爲了讓GC起做用,必須顯式的爲最後一個位置賦null值 elementData[--size] = null; //從列表中刪除的元素 return oldValue; }
注意:爲了讓GC起做用,必須顯式的爲最後一個位置賦null值。上面代碼中若是不手動賦null值,除非對應的位置被其餘元素覆蓋,不然原來的對象就一直不會被回收。
步驟:
/** * 用指定的元素替換此列表中指定索引的元素。 */ public E set(int index, E element) { //對index進行界限檢查 rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; //返回原來在這個位置的元素 return oldValue; }
細節:
移位運算符
簡介:移位運算符就是在二進制的基礎上對數字進行平移。按照平移的方向和填充數字的規則分爲三種:<<(左移)、>>(帶符號右移)和>>>(無符號右移)。
做用:對於大數據的2進制運算,位移運算符比那些普通運算符的運算要快不少,由於程序僅僅移動一下而已,不去計算,這樣提升了效率,節省了資源
好比這裏:int newCapacity = oldCapacity + (oldCapacity >> 1); 右移一位至關於除2,右移n位至關於除以 2 的 n 次方。這裏 oldCapacity 明顯右移了1位因此至關於oldCapacity /2。
另外須要注意的是:
內部類:
(1)private class Itr implements Iterator<E> (2)private class ListItr extends Itr implements ListIterator<E> (3)private class SubList extends AbstractList<E> implements RandomAccess (4)static final class ArrayListSpliterator<E> implements Spliterator<E>
因此這也能夠看出了 Iterator和ListIterator的區別:ListIterator在Iterator的基礎上增長了添加對象,修改對象,逆向遍歷等方法,這些是Iterator不能實現的。
參考資料:
https://blog.csdn.net/panweiw...
https://segmentfault.com/a/11...
https://github.com/Snailclimb...