List在工作中是很常用的一个集合
它有3个构造方法
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// 构建一个初始容量是10的ArrayList
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 根据给定的初始容量initialCapacity构建一个ArrayList
// 如果initialCapacity>0则直接直接创建容量为initialCapacity的ArrayList
// 如果initialCapacity=0则直接使用EMPTY_ELEMENTDATA空集合
// 如果initialCapacity<0,则出错。
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);
}
}
// 将给定的集合转换为ArrayList
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
}
平时我们都是这样声明的
List test = new ArrayList()
代表这里的第一种实例化方式,初始容量为10,当ArrayList中的元素超过10个以后,会重新分配内存空间,使数组的大小增长到16。
ps:在JDK1.5之前初始化的时候其实调用了this(10)给了初始大小,但是在JDK1.7之后如果new ArrayList()之后没有add元素进去,其实这个时候是没有进行初始化大小的.
只有在add第一个元素的时候,才会初始化大小为10,可以看到这里的size其实是一个变量,并不是List内的数组的长度,在扩容之后,把新add进去的这个元素放到之前数组长度的下一个index的位置
这个ensureCapacityInternal方法在后面第二次第三次扩容的时候也会用到,这里会和第一次的数组引用对象指定的地址进行比较,验证是不是第一次进行扩容
modCount代表当前List被调整大小的次数,这里的minCapacity看上图可知还是元素的个数(size) + 1,当元素个数第一次超过10的时候,第一次触发扩容
在扩容的时候,会拿原先的oldCapacity/2 + oldCapacity,这里这个oldCapacity可能为奇数,是偶数的话就是1.5倍了,第一次扩容的时候oldCapacity=10,newCapacity=15,因为数组下标是从0开始的,所以长度也就变成了16.
这里可以看到是用int表示的容量,那么就有可能因为数组太大而溢出,
int的最大值是2^31 - 1因为虽然int是4个字节(byte),1个字节8位(bit),就是32位,但是最高位被拿来当符号位了,正数0,负数1.那么根据二进制的求和公式,四位二进制1111=2 ^ 4 - 1=15,三十一位则为2 ^ 31 -1
3/2X = 2 ^ 31 -1(int的最大值)
当溢出了newCapacity - minCapacity < 0,那么就会取原先的size+1为新的数组长度,只扩容1个长度来存放元素,其实这个临界值可以计算出来,(2 ^ 31 -1)*2/3就是这个临界值
MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8据注释说是因为一些虚拟机会放一些头信息在里面
这个newCapacity最后+1+1一直+最后也会加到一个值,这个值肯定不能超过(2 ^ 31 -1)这里设置的是(2 ^ 31 -1)-8
当超过这个值的时候如果size+1已经溢出了这个时候会抛异常,不然就会和(2 ^ 31 -1)-8进行比较,进行三段式判断,可以看到最后的最大大小就是2 ^ 31 -1也就是int的最大值
最后就是利用Arrays.copyOf(elementData, newCapacity);把原数组copy到一个扩容之后的新数组里