ArrayList的扩容规则是变成原来最大容量的1.5倍+1
具体为什么,现在看一下源码:
ensureCapacityInternal是判断是否要扩容的方法,下面是源码:
elementData是存放元素的对象数组
EMPTY_ELEMENTDATA是空数组,表示现在ArrayList是空的
ensureCapacityInternal中首先是判断现在的ArrayList是不是空的,如果是空的,minCapacity就取默认的容量和传入的参数minCapacity中的大值
然后调用ensureExplicitCapacity方法,
modCount是fail fast机制,在jdk1.6之前都是有volatile来修饰的,尽可能的让并发访问非安全的集合对象时尽快的失败抛出异常,让程序员修改代码。
在jdk1.7中去掉了volatile修饰,因为感觉没有必要为非线程安全集合浪费效率,在jdk1.5开始就提供了线程安全的集合类,在多线程环境下就应该使用线程安全的集合。
接着看,如果minCapacity的值大于add数据之前的大小,就调用grow方法,进行扩容,否则什么也不做。
下面是具体看出来扩容机制的大小增长规则了:
注意:这里传过来的minCapcatiy的值是size+1,能够实现grow方法调用就肯定是(size+1)>elementData.length的情况,所以size就是初始最大容量或上一次扩容后达到的最大容量,所以才会进行扩容。
newCapacity=oldCapacity+(oldCapacity>>1),这里就是扩容大小确定的地方,相当于新的最大容量是 size+1+size/2 相当于原来的1.5倍然后加1
==============================================================================================================================
这样每次size达到容量后,再插入数据就会造成扩容,默认的容量大小是10,如果我们现在连续插入17的对象,就会扩容两次,第一次是在插入第11个对象时,第二次是在插入第17个对象时扩容,这样会频繁进行数组的拷贝,效率影响很大。那么我们在插入数据数量已知的情况,可以调用ensureCapacity方法。
下面是方法源码:
为什么要先判断先判断elementData!=EMPTY_ELEMENTDATA呢?现在看一下ArrayList的无参构造函数
这里看出来ArrayList在没有指定初始化容量的时候elementData就是一个空对象数组,没有任何对象元素,也就是容量为0,没有分配空间。
可以再看一下add方法中,调用的ensureCapacityInternal方法中判断elementData == EMPTY_ELEMENTDATA 是否为空,如果为空,minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);,minCapacity设置为其中的大值,在默认是空的时候,初次调用add方法传入的参数是1,也就是这里的minCapacity就是1,现在minCapcity变成了DEFAULT_CAPACITY 也就是10,下面会调用grow方法,然后执行 elementData = Arrays.copyOf(elementData, 10);下面注意拷贝方法:
执行了第一条语句后,copy=new Objecet[10]了,返回的结果数组大小就是10了, ArrayList相当于在没指定initialCapacity时就是会使用延迟分配对象数组空间,当第一次插入元素时才分配10个对象空间。
上面分析过程也包含了扩容的步骤了。
当我们已经确定了要插入的对象的数目(并不是在创建ArrayList之前就知道有多少对象要插入的情况),就应该首先调用ensureCapacity来一次性扩容到可以容得下要插入的对象个数,这样就避免的多次进行数组拷贝的情况,提高了效率,算是优化吧
当然,我们在创建ArrayList之前就知道有多少对象要插入就使用有参构造。