源碼解析
CopyOnWriteArrayList 首先它是實(shí)現(xiàn)了 List造虏,RandomAccess媒殉,Cloneable洪橘,Serializable
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
我們主要看看常用的方法add盛嘿、get娜饵、set、remove方法
首先是add方法膘融,每步都有注釋
//直接添加一個(gè)元素到數(shù)組的最后
public boolean add(E e) {
//給當(dāng)前的對(duì)象上鎖
final ReentrantLock lock = this.lock;
lock.lock();
try {
//首先得到數(shù)組
Object[] elements = getArray();
//得到原先數(shù)組的長(zhǎng)度
int len = elements.length;
//拷貝一份新的數(shù)組芙粱,長(zhǎng)度+1
Object[] newElements = Arrays.copyOf(elements, len + 1);
//把傳入的元素放置數(shù)組最后一位
newElements[len] = e;
//修改當(dāng)前對(duì)象的初始值
setArray(newElements);
return true;
} finally {
//釋放鎖
lock.unlock();
}
}
//根據(jù)下標(biāo)進(jìn)行添加元素
public void add(int index, E element) {
//上鎖
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//如果下標(biāo)大于當(dāng)前數(shù)組長(zhǎng)度或者小于0 則拋出下標(biāo)越界異常
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
Object[] newElements;
//得出要插入的下標(biāo)位置
int numMoved = len - index;
//為0表示在數(shù)組最后祭玉,直接插入
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + 1);
else {
// 否則表示要插入數(shù)組中間氧映,直接創(chuàng)建一份新的數(shù)組
newElements = new Object[len + 1];
// 先拷貝index前部分的內(nèi)容,在拷貝index后部分的內(nèi)容
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
//在把元素存入當(dāng)前下標(biāo)位置
newElements[index] = element;
//修改當(dāng)前對(duì)象的初始值
setArray(newElements);
} finally {
//釋放鎖
lock.unlock();
}
}
在來(lái)看看set方法
public E set(int index, E element) {
//上鎖
final ReentrantLock lock = this.lock;
lock.lock();
try {
//得到當(dāng)前的數(shù)組
Object[] elements = getArray();
//得到要修改下標(biāo)的值
E oldValue = get(elements, index);
//如果要修改的值不一樣
if (oldValue != element) {
int len = elements.length;
//重新拷貝一份脱货,修改下標(biāo)位置的值
Object[] newElements = Arrays.copyOf(elements, len);
newElements[index] = element;
setArray(newElements);
} else {
//說明要修改的值和原先一樣岛都,等于啥也沒干
// Not quite a no-op; ensures volatile write semantics
setArray(elements);
}
return oldValue;
} finally {
lock.unlock();
}
}
再來(lái)看看get方法,
//調(diào)用了下面的私有方法
public E get(int index) {
return get(getArray(), index);
}
private E get(Object[] a, int index) {
return (E) a[index];
}
private E get(Object[] a, int index) {
return (E) a[index];
}
最后是remove方法
public E remove(int index) {
//上鎖
final ReentrantLock lock = this.lock;
lock.lock();
try {
//得到當(dāng)前數(shù)組
Object[] elements = getArray();
//得到當(dāng)前的長(zhǎng)度
int len = elements.length;
//得到需要?jiǎng)h除下標(biāo)的值
E oldValue = get(elements, index);
int numMoved = len - index - 1;
//如果是在最后一位直接刪除
if (numMoved == 0)
setArray(Arrays.copyOf(elements, len - 1));
else {
//如果不在最后一位,那么就重新拷貝一份
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
//修改當(dāng)前對(duì)象的初始值
setArray(newElements);
}
return oldValue;
} finally {
//釋放鎖
lock.unlock();
}
}
總結(jié)
可以看到振峻,其中增刪改操作都是上了鎖的臼疫,因此CopyOnWriteArrayList主要是作用在讀多寫少的場(chǎng)景下,缺點(diǎn)就是如果是需要寫上立馬就能讀到的扣孟,那么不建議用這個(gè)烫堤,因?yàn)樽x是從舊的副本讀取的,而寫時(shí)存儲(chǔ)的數(shù)據(jù)是重新拷貝了一份新的數(shù)組了凤价,從而可能導(dǎo)致數(shù)據(jù)的不一致性鸽斟。