一、扩容
- 初始容量
调用ArrayList无参构造创建时,初始容量为0,往数组里添加元素,变成10。
- 之后扩容
1)调用add方法
ArrayList扩容,调用grow方法,原来的数组长度右移一位,容量是之前的1.5倍。
将旧数组copy到扩容后的新数组里,旧数组没有地方引用,会被垃圾回收。
2)调用addAll方法
一次扩容到位(在1.5倍扩容和addAll实际对象的size之间选一个较大的值)
总结:
ArrayList()会使用长度为0的数组;
ArrayList(int initialCapacity)会使用指定容量的数组;
public ArrayList(Collection<?extendsE> c)会使用c的大小作为数组容量;
add(Object o)首次扩容为10,再次扩容为上次容量的1.5倍;
addAll(Collection c)没有元素时,扩容为Math.max(10,实际元素个数),有元素时为Math.max(原容量1.5倍,实际元素个数)。
二、迭代器
fail-fast (ArrayList)
一旦发现遍历的同时有其他人来修改,则立刻抛出异常。
遍历时,调用next()方法,通过判断循环开始时modCount和循环进行中的expectedModCount,list修改次数是否一致,来抛异常。fail-safe (CopyOnWriteArrayList)
发现遍历的同时其他人来修改,应当能有应对策略,例如牺牲一致性来让整个遍历运行完成(遍历到的不是最新的元素)。
遍历数组时,先拷贝一份旧数组,再对旧数组进行遍历。
添加元素时,每次都复制一个旧数组,并+1,将添加的元素塞到最后一个,再赋值给旧数组。
调用迭代器的remove方法不会抛异常,因为它修改了expectedModCount为最新值。