Purpose
For the convenience of follow-up interview review, here is a personal study note record
source of learning materials
A learning video about high-frequency interview questions at Station B
Core knowledge points
Fail-fast: Concurrent modification is not supported. If concurrent modification occurs, ConcurrentModificationException will be thrown directly.
Fail-safe: Contrary to the above, concurrent modification is supported at the expense of consistency.
There is a check: checkForComodification()
ArrayList is a typical representative of Fail-fas, traverse It does not support modification at the same time, and fails as soon as possible.
CopyOnWritArrayList is a typical representative of Fail-safe, it can be modified while traversing, and the principle of separation of reading and writing.
source code excerpt
Part of the source code of ArrayList
ExpectedModCount is compared with modCount, if they are not equal, a concurrent modification exception (ConcurrentModificationException) is thrown directly
Partial source code of the iterator:
/**
* An optimized version of AbstractList.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;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
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];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
// 逻辑很简单就是2个modCount做一个对比
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
CopyOnWriteArrayList part of the source code
Iterator part source code
Here are all operations on the array of snapshots: snapshot
static final class COWIterator<E> implements ListIterator<E> {
/** Snapshot of the array */
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
private int cursor;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
public boolean hasNext() {
return cursor < snapshot.length;
}
@SuppressWarnings("unchecked")
public E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}
}
The implementation principle of the add method
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();
}
}
The idea of reading and writing separation
CopyOnWriteArrayList actually maintains two arrays, read: actually read the previous array, because of the snapshot. Write: write to the new data. The original snapshot array will be automatically garbage collected if it is not referenced later.