我的jdk版本
拆開源碼,我們從頭道來,不足或誤解,請指正
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList是一個泛型類,繼承自AbstractList,重寫了IndexOf(Object o)等方法,
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; }
實現了get(index),size()抽象方法
public T get(int index) { rangeCheck(index); return elementData(index); } public int size() { return size; }
實際上,ArrayList底層是這個玩意
transient Object[] elementData;//沒有被私有化是為了簡化內部類訪問
當然還有其他的幾個屬性:
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private int size;
在ArrayList中有三個構造器,分別為
//初始化容量的構造器 public ArrayList(int initCapacity){ if(initCapacity > 0) this.elementData = new Object[initCapacity]; else if(initCapacity == 0) this.elementData = EMPTY_ELEMENTDATA; else throw new IllegalArgumentException("cant init ArrayList for this initCapacity:"+initCapacity); } /*默認構造器*/ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * 傳入繼承集合類的實例 * @param c */ public ArrayList(Collection<? extends T> c) { elementData = c.toArray(); if((size = elementData.length) != 0){ if(elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size,Object[].class); } else { this.elementData = EMPTY_ELEMENTDATA; } }
注意的是,默認構造器中,
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
是一個默認的空Object數組
/** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
並沒有設置初始值,對於網上一片文章說ArrayList初始化長度為10,我又翻了一下jdk1.7源碼
/** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { super(); this.elementData = EMPTY_ELEMENTDATA; }
/** * Shared empty array instance used for empty instances.
* 這里是用final 和 static 修飾的 */ private static final Object[] EMPTY_ELEMENTDATA = {};
也是空數組,所以下面這種構造器最起碼不會出現在jdk1.7和jdk1.8中,也就是說兩個版本中的實例化ArrayList的時候沒有給他設置默認容量
public ArrayList() { this(10); }
那么,什么時候這個空數組才會真正的擴容呢,也就是大家說的容量為10的時候
在源碼中,ArrayList提供了這樣幾個方法,也就是關於ArrayList容量的關鍵的幾個方法
/*方法名直譯為:確認容量*/ public void ensureCapacity(int minCapacity) { //最小擴展 int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0: DEFAULT_CAPACITY; if(minCapacity > minExpand) ensureExplicitCapacity(minCapacity); }
上面的這個方法我是看了一陣子,腦回路差點跑偏,換種思路可能會更直觀一些
int minExpand = (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? DEFAULT_CAPACITY : 0 ;
我對這個方法的大概理解就是如果elementData是空的,返回一個默認容量(DEFAULT_CAPACITY),長度在上面已經定義好了,是10,如果傳入的參數大於這個最小擴展(10),
進入ensureExplicitCapacity()方法,
/*方法名直譯為:確認內部容量*/ public void ensureCapacityInternal(int minCapacity) { if(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) //取值 10 - 傳入最小值之間最大 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); ensureExplicitCapacity(minCapacity); }
判斷elementData是否為空數組,是則返回默認長度(10)和傳入參數中大的那一個
進入ensureExplicitCapacity()方法,
/* 明確容量*/ public void ensureExplicitCapacity(int minCapacity){ modCount++; //意識到長度溢出時,增長長度 if(minCapacity - elementData.length > 0) grow(minCapacity); };
以上兩個方法結束時調用本方法
在ArrayList,LinkedList,HashMap等等的內部實現增,刪,改中我們總能看到modCount的身影,modCount字面意思就是修改次數,而擁有這個參數的方法都是線程不安全的
在一個迭代器初始的時候會賦予它調用這個迭代器的對象的mCount,如何在迭代器遍歷的過程中,一旦發現這個對象的mcount和迭代器中存儲的mcount不一樣那就拋異常
這里涉及到了一個機制叫做:
fail-fast機制:
它是Java集合的一種錯誤檢測機制。當多個線程對集合進行結構上的改變的操作時,有可能會產生fail-fast機制。記住是有可能,而不是一定。例如:假設存在兩個線程(線程1、線程2),線程1通過Iterator在遍歷集合A中的元素,在某個時候線程2修改了集合A的結構(是結構上面的修改,而不是簡單的修改集合元素的內容),那么這個時候程序就會拋出 ConcurrentModificationException 異常,從而產生fail-fast機制。
/*有人說int最大減8是盡可能的避免一些錯誤 */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/** * 長度增長 * @param minCapacity */ public void grow(int minCapacity) { //原數組長度 int oldCapacity = elementData.length; //位運算 右位移1位 int newCapacity = oldCapacity + (oldCapacity >> 1); if(newCapacity - minCapacity < 0) //小於傳入長度時 newCapacity = minCapacity; if(newCapacity - MAX_ARRAY_SIZE > 0) //大於上方自定義的最大數組長度 newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); };
數組長度在增長時,有位移1並代表增加0.5倍,具體增長的數值,可自行百度java位運算
private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow 超出int整數范圍變負數 throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
了解到這幾個方法之后,我們來看一下常用的增/刪/改/查操作
查:
@SuppressWarnings("unchecked") T elementData(int index){ return (T) elementData[index]; }
//范圍檢測 public void rangeCheck(int index) { if(index >= size) { throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } }
/*來自父類的抽象方法*/ public T get(int index) { rangeCheck(index);//檢測傳入參數范圍,超過范圍數組下標越界 return elementData(index); }
沒什么好說的,只是代碼抽象化了一點
增
private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
public boolean add(T t) { ensureCapacityInternal(size + 1); elementData[size++] = t; return true; }
public void add(int index, T element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // 增加容量 System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++;//??為什么又加了 }
改:
public T set(int index, T element) { rangeCheck(index); T oldValue = elementData(index);//這里的elementData是方法 elementData[index] = element;//這里的elementData是數組 return oldValue; }
//這里的返回值是被替換的元素
刪:
public T remove(int index) { rangeCheck(index); modCount++; T oldValue = elementData(index);//方法 int numMoved = size - index -1;//是否超長 if(numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null;//最后一個設置為null 數組 return oldValue; }
到這里ArrayList一些基礎內容也講的差不多了,更為深入的一些機制現在就不提了,以后再深入研究吧
學習是一種態度,不管是jdk源碼還是spring或者tomcat源碼,抱着借鑒和模仿的心態,努力提高自己.
寫代碼應該是一件很快樂的事情,過程中也許會有些痛苦,但是往往快樂最后遠大於痛苦
不要因為別人一句看這玩意有什么用而放棄自己的追求,不要被外界干擾,自己要堅定