CopyOnWriteArrayList是ArrayList的線程安全版本,內部也是經過數組實現,每次對數組的修改都徹底拷貝一份新的數組來修改,修改完了再替換掉老數組,這樣保證了只阻塞寫操做,不阻塞讀操做,實現讀寫分離。java
/** 用於修改時加鎖 */ final transient ReentrantLock lock = new ReentrantLock(); /** 真正存儲元素的地方,只能經過getArray()/setArray()訪問 */ private transient volatile Object[] array;
建立空數組數組
public CopyOnWriteArrayList() { // 全部對array的操做都是經過setArray()和getArray()進行 setArray(new Object[0]); } final void setArray(Object[] a) { array = a; }
若是c是CopyOnWriteArrayList類型,直接把它的數組賦值給當前list的數組,注意這裏是淺拷貝,兩個集合共用同一個數組。
若是c不是CopyOnWriteArrayList類型,則進行拷貝把c的元素所有拷貝到當前list的數組中。安全
public CopyOnWriteArrayList(Collection<? extends E> c) { Object[] elements; if (c.getClass() == CopyOnWriteArrayList.class) // 若是c也是CopyOnWriteArrayList類型 // 那麼直接把它的數組拿過來使用 elements = ((CopyOnWriteArrayList<?>)c).getArray(); else { // 不然調用其toArray()方法將集合元素轉化爲數組 elements = c.toArray(); // 這裏c.toArray()返回的不必定是Object[]類型 // 詳細緣由見ArrayList裏面的分析 if (elements.getClass() != Object[].class) elements = Arrays.copyOf(elements, elements.length, Object[].class); } setArray(elements); }
把toCopyIn的元素拷貝給當前list的數組。dom
public CopyOnWriteArrayList(E[] toCopyIn) { setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class)); }
添加一個元素到末尾。性能
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(); } }
添加一個元素在指定索引處。this
public void add(int index, E element) { final ReentrantLock lock = this.lock; // 加鎖 lock.lock(); try { // 獲取舊數組 Object[] elements = getArray(); int len = elements.length; // 檢查是否越界, 能夠等於len if (index > len || index < 0) throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+len); Object[] newElements; int numMoved = len - index; if (numMoved == 0) // 若是插入的位置是最後一位 // 那麼拷貝一個n+1的數組, 其前n個元素與舊數組一致 newElements = Arrays.copyOf(elements, len + 1); else { // 若是插入的位置不是最後一位 // 那麼新建一個n+1的數組 newElements = new Object[len + 1]; // 拷貝舊數組前index的元素到新數組中 System.arraycopy(elements, 0, newElements, 0, index); // 將index及其以後的元素日後挪一位拷貝到新數組中 // 這樣正好index位置是空出來的 System.arraycopy(elements, index, newElements, index + 1, numMoved); } // 將元素放置在index處 newElements[index] = element; setArray(newElements); } finally { // 釋放鎖 lock.unlock(); } }
添加一個元素若是這個元素不存在於集合中。spa
public boolean addIfAbsent(E e) { // 獲取元素數組, 取名爲快照 Object[] snapshot = getArray(); // 檢查若是元素不存在,直接返回false // 若是存在再調用addIfAbsent()方法添加元素 return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false : addIfAbsent(e, snapshot); } private boolean addIfAbsent(E e, Object[] snapshot) { final ReentrantLock lock = this.lock; // 加鎖 lock.lock(); try { // 從新獲取舊數組 Object[] current = getArray(); int len = current.length; // 若是快照與剛獲取的數組不一致 // 說明有修改 if (snapshot != current) { // 從新檢查元素是否在剛獲取的數組裏 int common = Math.min(snapshot.length, len); for (int i = 0; i < common; i++) // 到這個方法裏面了, 說明元素不在快照裏面 if (current[i] != snapshot[i] && eq(e, current[i])) return false; if (indexOf(e, current, common, len) >= 0) return false; } // 拷貝一份n+1的數組 Object[] newElements = Arrays.copyOf(current, len + 1); // 將元素放在最後一位 newElements[len] = e; setArray(newElements); return true; } finally { // 釋放鎖 lock.unlock(); } }
獲取指定索引的元素,支持隨機訪問,時間複雜度爲O(1)。線程
public E get(int index) { // 獲取元素不須要加鎖 // 直接返回index位置的元素 // 這裏是沒有作越界檢查的, 由於數組自己會作越界檢查 return get(getArray(), index); } final Object[] getArray() { return array; } private E get(Object[] a, int index) { return (E) a[index]; }
刪除指定索引位置的元素。code
public E remove(int index) { final ReentrantLock lock = this.lock; // 加鎖 lock.lock(); try { // 獲取舊數組 Object[] elements = getArray(); int len = elements.length; E oldValue = get(elements, index); int numMoved = len - index - 1; if (numMoved == 0) // 若是移除的是最後一位 // 那麼直接拷貝一份n-1的新數組, 最後一位就自動刪除了 setArray(Arrays.copyOf(elements, len - 1)); else { // 若是移除的不是最後一位 // 那麼新建一個n-1的新數組 Object[] newElements = new Object[len - 1]; // 將前index的元素拷貝到新數組中 System.arraycopy(elements, 0, newElements, 0, index); // 將index後面(不包含)的元素往前挪一位 // 這樣正好把index位置覆蓋掉了, 至關於刪除了 System.arraycopy(elements, index + 1, newElements, index, numMoved); setArray(newElements); } return oldValue; } finally { // 釋放鎖 lock.unlock(); } }
返回數組的長度。對象
public int size() { // 獲取元素個數不須要加鎖 // 直接返回數組的長度 return getArray().length; }