failFast机制:这个机制主要用于迭代器、加强for循环等相关功能,也就是一个线程在迭代一个容器 时,如果其他线程改变了容器内的元素,迭代的这个线程会抛 出“ConcurrentModificationException”异常
ArrayList LinkedList 均适用了此机制
ArrayList
ArrayList具有数组的特点:
- 可快速查询, 可通过下标进行查询
- 插入慢
因为ArrayList需要随时进行Object数组容量大小检查,如果数组容量不能满足新插入元素所需要的大小需求,那么就会触发自动扩容,将大小扩展为原来的1.5倍,在将原来的数组拷贝到新的数组中,然后在添加元素,删除元素时也可能会涉及到数组拷贝
不是线程安全的, 因为内部维护了modCount变量, 此变量并没有使用volatile进行修饰, 因此当多个线程进行操作arrayList时, modCount的值将无法保证一致, 因此抛出ConcurrentModificationException异常(因为在进行读写之前均会对modCount进行校验)
与之对应的
Vector
是线程安全的,因为在很多方法上都使用了synchronized
关键字, 因此性能较ArrayList较低; 底层也是使用的数组方式.(很少使用)
方法定义
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
重要的成员变量
private static final int DEFAULT_CAPACITY = 10; // 默认初始化大小
protected transient int modCount = 0; // 记录对象被修改的次数
transient Object[] elementData; // 使用数据结构
元素最大值
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
扩容
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);// oldCapacity >> 1 原来数组的一半, 也就是扩容为原来的1.5倍 使用位运算效率更高
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
浅拷贝的Clone
此方法并不是对应List接口中定义的方法;
因为是浅拷贝, 因此新的对象与老对象同时指向同一快内存, 无论修改那个对应的另一个的内容也会修改
/**
* Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
* elements themselves are not copied.)
*
* @return a clone of this <tt>ArrayList</tt> instance
*/
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
迭代器
如果一个ArrayList对象生成对应的迭代器之后, 并不能在使用add()方法, 如果在使用, 则在使用迭代器进行遍历的时候将会在
第2处
出现错误;原因: 因为一个对象当调用
Iterator iterator = al.iterator();
当时候会对Itr类进行初始化, 同时会为新的Itr对象中的成员变量进行初始化 也就是
- cursor为int默认值0
- lastRet 初始化值为-1
- 最重要的是 expectedModCount 初始化的值会获取对应的ArrayList (al)对象的modCount的值
但是当调用
al.iterator()
此方法后再次调用al.add()方法 al中的modCount值会自增, 但是已生成的的迭代器中的expectedModCount的值没有进行更新, 因此在调用next()时会调用checkForComodification()
进行校验expectedModCount 与 modCount的值是否相等, 如果不相等则直接抛出异常
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
...
public Iterator<E> iterator() {
return new Itr();
}
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; // -------第1处
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();// -------- 第2处
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];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet ;// 使用迭代器进行删除
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount; // 使用迭代器删除ArrayList中的元素时需要修改迭代器中expextedModCount的值, 否则删除后在使用next()方法会出错
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
...
final void checkForComodification() {
if (modCount != expectedModCount)// modCount 是ArrayList的成员变量, 用于记录此对象修改的次数
throw new ConcurrentModificationException();
}
}
}
subList
经常问的问题: 为什么subList对象同时也会修改原是的ArrayList对象?
主要的原因:SubList是ArrayList的一个内部类
从代码上看: 在1⃣️处我们可以看到最终调用了
parent.add()
, 通过2⃣️ 3⃣️ 可以看的出来parent就是对应的Ar rayList对象, 因此subList进行增加元素 同时原始arrayList也会变化;从SubList类的定义中可以看出是AbstractList的子类, 但是SubList仅仅只重写了
public void add(int index, E element)
, 而第一个参数index 是SubList对象中的size大小
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
...
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);// ------- 2⃣️
}
private class SubList extends AbstractList<E> implements RandomAccess {
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent; // -----3⃣️
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
public void add(int index, E e) {
rangeCheckForAdd(index);
checkForComodification();
parent.add(parentOffset + index, e); // parent.add调用的就是ArraryList.add方法 ---- 1⃣️
this.modCount = parent.modCount;
this.size++;
}
private void checkForComodification() {
if (ArrayList.this.modCount != this.modCount)
throw new ConcurrentModificationException();
}
...
}
}
// AbstractList
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
...
public boolean add(E e) {
add(size(), e);
return true;
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
...
}
LinkedList
具有Link的特点:
- 快速插入
- 查询慢
不是线程安全的, 因为内部维护了modCount变量, 此变量并没有使用volatile进行修饰, 因此当多个线程进行操作arrayList时, modCount的值将无法保证一致, 因此抛出ConcurrentModificationException异常(因为在进行读写之前均会对modCount进行校验)
类定义
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
重要的成员变量
protected transient int modCount = 0; // 记录对象被修改的次数
// 使用的数据结构
transient Node<E> first;
transient Node<E> last;
因为使用的是链表, 因此没有扩容一说; add
remove
方法也都是链表的修改 简单没有什么特别需要讲解的