ArrayList介绍
public class ArrayList extends AbstractList
implements List, 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 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