ArrayList介紹
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
private int size;
ArrayList維護了兩個數組DEFAULT_CAPACITY和elementData。DEFAULT_CAPACITY是一個空數組,當創建一個空的ArrayList的時候就會使用DEFAULT_CAPACITY,public ArrayList() { super(); this.elementData = EMPTY_ELEMENTDATA; }
可以看出在這裏創建出ArrayList的時候容量是爲0的(並不是10嗷),當在容器中添加一個元素以後,則會使用elementData來存儲數據。只要關注好elementData數組就好啦。
擴容機制
這裏就主要介紹ArrayList的擴容機制啦,當調用到add和addAll和readObject就有可能進行擴容操作,這裏就用add方法舉例(只添加一個元素,其他同理)
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
當往集合添加一個元素時就會將添加後所需要的容量大小進行判斷,也就是方法ensureCapacityInternal啦!
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
注意這裏,前面說的當你創建的是一個空ArrayList的時候,就會將你的容量變爲DEFAULT_CAPACITY(10),或者當你往空集合里加入10個以上的元素時,集合容量將會變成你加入的元素個數。之後就是ensureExplicitCapacity方法啦!
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
當你的元素所需最小容量已經大於elementData的數組最大長度時就需要進行擴容操作啦,也就是grow方法
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
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);
}
首先就給容量進行1.5倍擴容後賦值給newCapacity ,擴容1.5倍還不夠的話就直接用你所需的容量咯!要是容量快達到int最大大小(2^31-1)的話就進入到這個方法hugeCapacity。
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
線程不安全實例
知道ArrayList是自動擴容的之後,按理說是不大會出現數組越界這個問題啦。但事實不是。下面就舉個小例子來測試一下。代碼如下
public class ArraryListThreadTest {
static ArrayList<Integer> list = new ArrayList<>();
static class AddToList implements Runnable {
int num = 0;
public void run() {
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add(num);
System.out.println(
Thread.currentThread().getName() + " add num is " + num + " list'size is " + list.size());
num += 2;
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new AddToList());
Thread t2 = new Thread(new AddToList());
t1.start();
t2.start();
}
}
輸出結果爲:
Thread-0 add num is 0 list'size is 2
Thread-1 add num is 0 list'size is 2
Thread-1 add num is 2 list'size is 4
Thread-0 add num is 2 list'size is 4
Thread-1 add num is 4 list'size is 6
Thread-0 add num is 4 list'size is 6
Thread-0 add num is 6 list'size is 8
Thread-1 add num is 6 list'size is 8
Thread-1 add num is 8 list'size is 10
Thread-0 add num is 8 list'size is 10
Thread-1 add num is 10 list'size is 11
Thread-0 add num is 10 list'size is 11
Thread-0 add num is 12 list'size is 13
Thread-1 add num is 12 list'size is 13
Thread-1 add num is 14 list'size is 14
Thread-0 add num is 14 list'size is 14
Thread-1 add num is 16 list'size is 15
Thread-0 add num is 16 list'size is 15
Thread-0 add num is 18 list'size is 17
Thread-1 add num is 18 list'size is 17
Thread-0 add num is 20 list'size is 19
Thread-1 add num is 20 list'size is 19
Thread-0 add num is 22 list'size is 20
Thread-1 add num is 22 list'size is 20
Thread-1 add num is 24 list'size is 21
Thread-0 add num is 24 list'size is 21
Thread-1 add num is 26 list'size is 23
Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: 22
at java.util.ArrayList.add(ArrayList.java:444)
at conllection.ArraryListThreadTest$AddToList.run(ArraryListThreadTest.java:19)
at java.lang.Thread.run(Thread.java:745)
爲什麼在elementData長度22的時候出現數組越界異常呢?
衆所周知ArrayList是線程不安全的嘛(沒實現Serializable就是慘。。。。)
由於沒有該方法沒有同步,導致出現這樣一種現象,用下標爲22時的異常舉例。當集合中已經添加了21個元素時,一個線程率先進入add()方法,在執行ensureCapacityInternal(size + 1)時,發現還可以添加一個元素,故數組沒有擴容,但隨後該線程被阻塞在此處。接着另一線程進入add()方法,執行ensureCapacityInternal(size + 1),由於前一個線程並沒有添加元素,故size依然爲21,依然不需要擴容,所以該線程就開始添加元素,使得size++,變爲22,數組已經滿了。而剛剛阻塞在elementData[size++] = e;語句之前的線程開始執行,它要在集合中添加第23個元素,而數組容量只有22個,所以就發生了數組下標越界異常!
關於ArrayList的越界問題參考文章https://www.cnblogs.com/smellpawn/p/10841480.html