目录
2、Interator 迭代器的 FailFast和FailSafe
List
1、ArrayList扩容规则
默认无参构造 初始容量为0 每次扩容1.5倍
如果是有参构造传入的是数值 初始容量为指定容量的数组 每次扩容1.5倍的
如果有参构造传入的是集合 初始容量就是集合的大小 每次扩容1.5倍的
add 方法(针对无参的构造) 首次扩容为10, 每次扩1.5倍的
addAll (针对无参构造) 扩容为Math.max(10,实际原始个数)
有参构造 如果有元素时为Math.max(原容量的1.5倍,实际元素个数)
2、Interator 迭代器的 FailFast和FailSafe
-
FailFast :一旦发送遍历的同时其他人来修改数据,立即抛出异常
-
FailSafe: 发现遍历的同时其他人来修改数据,应当有应对策略,例如 牺牲一致性让整个遍历运行完成
ArrayList的迭代器是属于FailFast的
增强for循环底层就是迭代器
public class FailFastVsFailSafe {
@Test
public void failFast() {
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
1 for (Object o : list) {
System.out.println(o);
}
}
@Test
public void failSafe() {
CopyOnWriteArrayList list = new CopyOnWriteArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
2 for (Object o : list) {
System.out.println(o);
}
}
}
FailFast实现的源码位置:
在1处的for打上断点进入
public Iterator<E> iterator() { return new Itr(); } 增强for循环 底层是迭代器
private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; 这是关键 list添加4次 所以modCount(记录list修改次数)为4 将值赋给expectedModCount
public E next() { checkForComodification(); //这个方法是每次遍历的时候进行检查 int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; }
checkForComodification
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } 判断expectedModCount与modCount是否相等 expectedModCount是4固定了 如果在遍历图中修改list,modCount值就会增加 然后这两个值不相等 也就抛出异常了
FailSafe实现的源码位置:
在2处的for打上断点进入
public Iterator<E> iterator() { return new COWIterator<E>(getArray(), 0); }private COWIterator(Object[] elements, int initialCursor) { cursor = initialCursor; snapshot = elements; //将需要遍历的数组保存在snapshot中 }
然后我们在循环的时候添加数组 发现list的数组变了
而snapshot还是原来的数组
因此遍历的时候还是输出原先的数组元素
输出集合 元素就含新增的了 (读写是分离的)
这个可以看CopyOnWriteArrayList 的add方法
public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; //每次都是 复制原来数组长度加1到新的数组 Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); } }
总结
ArrayList是 failFast的典型代表,遍历的同时不能修改,否则会抛出异常
CopyOnWriteArrayList 是failSafe的典型代表,遍历的同时可以修改,原理是读写分离
3、ArrayList和LinkedList的比较
-
ArrayLsit
-
基于数组(可变数组),需要连续的空间
-
随机访问 (根据索引访问) 实现了RandomAccess接口(标记接口) 代表可以随机访问
-
尾部插入、删除性能可以,其他部分插入删除都会移动数据,因此性能比较低
-
可以利用cpu缓存,局部性原理
-
-
LinkedList
-
基于双向链表,无序连续内存
-
顺序访问 (需要沿着链表一个一个找)
-
头尾插入删除的性能高(中间涉及访问位置 一个一个走)
-
占用内存多(有数据域和指针域等)
-