ArrayList:
扩容规则:
-
ArrayList() 空参构造会使用长度为零的数组
-
ArrayList(int initialCapacity) 会使用指定容量的数组
-
public ArrayList(Collection<? extends E> c) 会使用 c 的大小作为数组容量
-
add(Object o) 首次扩容为 10,再次扩容为上次容量的 1.5 倍
-
addAll(Collection c) 没有元素时,扩容为 Math.max(10, 实际元素个数),有元素时为 Math.max(原容量 1.5 倍, 实际元素个数)
Iterator:
fail-fast和fail-safe:
-
ArrayList 是 fail-fast 的典型代表,线程不安全,遍历的同时不能修改,尽快失败
遍历时无法修改:因为底层在调用迭代器是首先记录了ArrayList的修改次数,并且将这个次数保存到迭代器成员属性中,在每次迭代遍历时首先会判断数组的修改次数和刚传入时的修改次数是否一致,不一致则会抛出异常。例如:使用空参构造创建了一个ArrayList对象,并用add方法向其中加入了三个元素,此时原ArrayList修改次数(modCount)为3,迭代器会记录当前这个修改次数,保存在expectedModCount中,如果此时对原List修改,会改变modCount的值,使其与expectedModCount的值不相等,在检查时抛出异常。 -
CopyOnWriteArrayList 是 fail-safe 的典型代表,线程安全,遍历的同时可以修改,原理是读写分离
遍历时可以修改:在调用迭代器遍历时,迭代器会先将此时的元素保存到其内部的一个final数组中,此后的迭代过程全是相对于这个内部的final数组进行的,也就是说如果在迭代过程中对原List进行修改,迭代会继续进行,不过对于新修改的部分迭代器时遍历不出来的,只能迭代出修改之前的List。
LinkedList:
实现:
底层依靠双向链表来实现的。一般使用ArrayList即可。
ArrayList和LinkedList比较:
ArrayList | LinkedList | |
内存分配 | 基于数组,需要连续内存 | 基于双向链表,无需连续内存 |
随机访问 | 快(通过索引) | 慢(遍历链表) |
插入数据 | 尾部插入、删除性能可以,其它部分插入、删除都会移动数据,因此性能会低 | 头尾插入、删除性能高,中间性能低 |
cpu局部性 | 有效利用 | 占用内存多 |