使用一個容器固然要從容器的構造開始,ArrayList重載了三種構造函數html
平常中最常使用的是無參數構造函數,使用另外一個ArrayList來構造新的ArrayList在諸如回溯算法中也很常見。java
public ArrayList() public ArrayList(int initialCapacity) public ArrayList(Collection<? extends E> c)
無參構造函數中將elementData
賦值爲DEFAULTCAPACITY_EMPTY_ELEMENTDATA
(即空數組),其中elementData
就是ArrayList存放元素的真實位置。也能夠在初始化時將容器容量肯定爲傳入的int參數。算法
//類中定義的變量 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; transient Object[] elementData; // non-private to simplify nested class access,若是是私有變量,在內部類中獲取會比較麻煩 //無參構造 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); } }
toArray()
方法實現可能不一樣,返回值不必定爲Object[]
,即elementData
的類型會發生變化(例子見ClassTypeTest.java)。因此須要進行類型判斷,若elementData.getClass() != Object[].class
則使用Arrays工具類中的copyOf方法將elementData
的類型改回。public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // defend against c.toArray (incorrectly) not returning Object[] // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
//ClassTypeTest.java public class ClassTypeTest { public class Person{ } public class Student extends Person{ } public static void main(String[] args) { Person[] p = new Student[5]; System.out.println(p.getClass()); } } //output: //class [LClassTypeTest$Student;
容器的本質無非是替咱們保管一些咱們須要儲存的數據(基本數據類型、對象),咱們能夠往容器里加入,也能夠從容器裏獲取,也能夠刪除容器內元素。使用容器而不是數組是由於數組對於咱們使用來講過於不便利數組
ArrayList容器底層是基於數組實現,可是咱們使用的時候卻不須要關心數組越界的問題,是由於ArrayList實現了數組的動態擴容,從add方法出發查看ArrayList是怎麼實現的安全
elementData
數組擴容操做開始於 add方法,當grow()返回擴容後的數組,add方法在這個數組上進行添加(插入)操做。在add方法中看到的modCount變量涉及 Java 的 fail-fast 機制,將在本文後面進行講解//size是ArrayList實際添加的元素的數量,elementData.length爲ArrayList能最多容納多少元素的容量 //經過代碼能夠看出,當size==elementData.length時,容器沒法再放入元素,因此此時須要一個新的、更大的elementData數組 private int size; public boolean add(E e) { modCount++; add(e, elementData, size); return true; } private void add(E e, Object[] elementData, int s) { if (s == elementData.length) elementData = grow(); elementData[s] = e; size = s + 1; }
minCapacity
個元素(即容量比原來至少大minCapacity
)private static final int DEFAULT_CAPACITY = 10; private Object[] grow() { return grow(size + 1); } private Object[] grow(int minCapacity) { int oldCapacity = elementData.length; if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { int newCapacity = ArraysSupport.newLength(oldCapacity, minCapacity - oldCapacity, /* minimum growth */ oldCapacity >> 1 /* preferred growth */); return elementData = Arrays.copyOf(elementData, newCapacity); } else { // 當oldCapacity==0 || elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA 時進入該分支 // 即容器使用無參構造函數 或 new ArrayList(0)等狀況時進入 // elementData數組大小被擴容爲 10 return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)]; } }
prefGrowth=oldCapacity/2
,由此處可看出大部分狀況下擴容後的數組大小爲原數組的1.5倍
newLength - MAX_ARRAY_LENGTH <= 0
進行判斷,不能使用 newLength <= MAX_ARRAY_LENGTH
進行判斷,若是 newLength
超過 2147483647 ,會溢出爲負值,此時newLength
依舊小於MAX_ARRAY_LENGTH
。而用newLength - MAX_ARRAY_LENGTH <= 0
則是至關於將newLength
這個數字在「int環」上向左移動了MAX_ARRAY_LENGTH
位,若這個數字此時爲負數(即落在綠色區域),則直接返回當前newLength
,不然進入hugeLength方法。public static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8; public static int newLength(int oldLength, int minGrowth, int prefGrowth) { // assert oldLength >= 0 // assert minGrowth > 0 int newLength = Math.max(minGrowth, prefGrowth) + oldLength; //!!! 判斷數組大小是否超過int值容許的大小 if (newLength - MAX_ARRAY_LENGTH <= 0) { return newLength; } return hugeLength(oldLength, minGrowth); } private static int hugeLength(int oldLength, int minGrowth) { int minLength = oldLength + minGrowth; if (minLength < 0) { // overflow throw new OutOfMemoryError("Required array length too large"); } if (minLength <= MAX_ARRAY_LENGTH) { return MAX_ARRAY_LENGTH; } return Integer.MAX_VALUE; }
public boolean addAll(Collection<? extends E> c)
方法以及它的重載public boolean addAll(int index, Collection<? extends E> c)
方法//刪除元素: public E remove(int index) public boolean remove(Object o) public boolean removeAll(Collection<?> c) boolean removeIf(Predicate<? super E> filter, int i, final int end) public void clear() //修改元素: public E set(int index, E element) public void replaceAll(UnaryOperator<E> operator) //查詢/得到元素: public E get(int index) public int indexOf(Object o) public List<E> subList(int fromIndex, int toIndex)
根據官方文檔的描述,ArrayList是一個非線程安全的容器,兩個線程能夠同時對一個ArrayList進行讀、寫操做。一般來講對封裝了ArrayList的類進行了同步操做後就能確保線程安全。多線程
Note that this implementation is not synchronized. If multiple threads access an
ArrayList
instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements,or explicitly resizes the backing array; merely setting the value of an element is not a structural modification.)ide
固然,ArrayList實現中也經過fail-fast確保了不正確的多線程操做會盡快的拋出錯誤,防止Bug隱藏在程序中直到將來的某一天被發現。函數
modCount
,該變量在ArrayList執行結構性的修改(structural modification)時會 +1,如add、remove、clear等改變容器size的方法,而在set方法中不自增變量(但使人迷惑的是replaceAll和sort方法卻會修改modCount
的值,總結來講不該該依賴modCount實現的fail-fast機制)//java.util.AbstractList.java protected transient int modCount = 0;
modCount
賦值給一個expectedModCount
變量,在對兩個容器內的元素一一進行完比較判斷後得出兩個對象是否相等的判斷,但在返回判斷以前要問一個問題,在對比判斷的過程當中當前這個ArrayList(this)有沒有被其餘人(線程)動過?因此加了一個checkForComodification
方法進行判斷,若是modCount
與原先不一樣則表明該ArrayList通過改動,則equals的判斷結果並不可信,拋出throw new ConcurrentModificationException()
異常//java.util.ArrayList.java public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof List)) { return false; } final int expectedModCount = modCount; // ArrayList can be subclassed and given arbitrary behavior, but we can // still deal with the common case where o is ArrayList precisely boolean equal = (o.getClass() == ArrayList.class) ? equalsArrayList((ArrayList<?>) o) : equalsRange((List<?>) o, 0, size); checkForComodification(expectedModCount); return equal; } private void checkForComodification(final int expectedModCount) { if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } }
我使用代碼模擬了在使用迭代器的狀況下throw new ConcurrentModificationException()
的拋出工具
public class failFastTest_02 { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { List<Integer> list = new ArrayList<>(); int changeIndex = 5; for(int i=0;i<10;i++){ list.add(i); } Iterator iterator = list.iterator(); //反射獲取expectedModCount Field field = iterator.getClass().getDeclaredField("expectedModCount"); field.setAccessible(true); //反射獲取modCount Class<?> l = list.getClass(); l = l.getSuperclass(); Field fieldList = l.getDeclaredField("modCount"); fieldList.setAccessible(true); while(iterator.hasNext()){ if(changeIndex==0){ list.add(-42); } System.out.println("Value of expectedModCount:" + field.get(iterator)); System.out.println("Value of modCount:" + fieldList.get(list)); System.out.println("iterator get element in list "+ iterator.next()); changeIndex--; } } }
getClass()方法來獲取類的定義信息,經過定義信息再調用getFields()方法來獲取類的全部公共屬性,或者調用getDeclaredFields()方法來獲取類的全部屬性,包括公共,保護,私有,默認的方法。可是這裏有一點要注意的是這個方法只能獲取當前類裏面顯示定義的屬性,不能獲取到父類或者父類的父類及更高層次的屬性的。使用Class.getSuperClass()獲取父類後再獲取父類的屬性。源碼分析
expectedModCount
不會由於ArrayList方法對列表的修改而改變,在這以後對於該列表(ArrayList)的結構性修改都會致使異常的拋出,這確保了迭代器不會出錯(迭代器使用 cursor
維護狀態,當外界的結構變化時 size
改變,不使用fail-fast public boolean hasNext() {return cursor != size;}
可能會產生錯誤結果),若是想在使用迭代器時修改列表,應該使用迭代器自帶的方法。上述代碼報錯以下。 cursor
顧名思義跟光標同樣,讀取一個元素後要將光標向後移動一格,刪除一個元素則是將光標前的一個元素刪除,此時光標隨之退後一格。固然,ArrayList迭代器不能一直退格(remove),必需要先能讀取一個元素而後才能將其刪除elementData
數組中,使用無參構造函數時,加入第一個元素後elementData
數組大小爲10。new ArrayList<>().size()
爲列表儲存真實元素個數,不爲列表容量fail-fast相關:http://www.javashuo.com/article/p-kutmancs-ne.html
https://baijiahao.baidu.com/s?id=1638201147057831295&wfr=spider&for=pc
內部類訪問外部類私有變量:https://blog.csdn.net/qq_33330687/article/details/77915345