1. 程式人生 > 實用技巧 >探索CopyOnWriteArrayList底層實現

探索CopyOnWriteArrayList底層實現

簡單介紹

由於CopyOnWriteArrayList註釋並不是很多,所以在這裡簡單的說明下,它屬於執行緒安全,底層是通過生成陣列的新副本來實現的,也就是在修改列表元素/結構的情況會生成新副本。簡單地說,它是ArrayList的一個變體!探索CopyOnWriteArrayList原始碼是基於JDK1.8版本的。

開幹

開始進入到看原始碼的時間吧!

資料結構


    //支援隨機訪問,可克隆,可序列化
    public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {

        //序列號
        private static final long serialVersionUID = 8673264195747942595L;

        //目前先知道是鎖就行了,後續會有新文章來進行詳細說明
        //保證同一個時間內只有一個執行緒能訪問,
        final transient ReentrantLock lock = new ReentrantLock();

        //保證變數的可見性,但無法保證原子性
        //至於什麼是可見性、原子性,較為難理解,況且也不是本章的重點,加上作者對其的理解還不夠,所以後續才會出文章去做詳細說明
        //該陣列用於存放元素
        private transient volatile Object[] array;

        //相比於ArrayList,為什麼沒有了size、modCount成員屬性呢?
        //因為每次新增/刪除元素時,都會生成陣列的新副本,也就是說新副本代替了size的作用
        //modCount在ArrayList中主要用於在迭代器的結構修改判斷中,而CopyOnWriteArrayList的迭代器中不支援結構修改,為什麼不支援呢?
        //原始碼中並未提到為什麼不支援,以下主要是自我的理解
        //結構修改中必然涉及到加鎖,若對迭代器加鎖了,要是對它進行遍歷上千條資料,那其他執行緒就不用執行了,所以迭代器萬不可加鎖!
        //不能加鎖,那對於結構的修改勢必會造成併發訪問的問題,所以目前是沒有提供支援,純屬個人理解!

        //JDK1.8並未有該類的相關注釋,偏向底層,目前作者只知道它是跟鎖有關聯的,若想知道可自行百度
        private static final sun.misc.Unsafe UNSAFE;
        private static final long lockOffset;
        static {
            try {
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                Class<?> k = CopyOnWriteArrayList.class;
                lockOffset = UNSAFE.objectFieldOffset
                    (k.getDeclaredField("lock"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }

    }

建構函式


    /**
     * 建立空陣列
     */
    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }

    /**
     * 構建一個包含指定collection集合的物件,CopyOnWriteArrayList容量大小和該集合大小一致,指定集合中的元素按照迭代器的順序排列
     * collection集合型別有Map、set、List等子類,所以入參可以是多種型別
     * CopyOnWriteArrayList保證陣列中元素型別是Object
     * @param c 指定集合
     */
    public CopyOnWriteArrayList(Collection<? extends E> c) {
        Object[] elements;
        if (c.getClass() == CopyOnWriteArrayList.class)
            elements = ((CopyOnWriteArrayList<?>)c).getArray();
        else {
            elements = c.toArray();
            //c.toArray 可能不會返回正確的Object[]型別,這邊可能會利用多型的性質,如 A a = new B()
            if (elements.getClass() != Object[].class)
                elements = Arrays.copyOf(elements, elements.length, Object[].class);
        }
        setArray(elements);
    }

    /**
     * 構建一個包含指定陣列的物件
     * 生成陣列的新副本,並讓該物件中的陣列指向它
     * @param toCopyIn 指定陣列
     */
    public CopyOnWriteArrayList(E[] toCopyIn) {
        setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
    }

方法說明

接下來按照類的宣告順序介紹方法,有必要的情況下結合例子進行說明。

簡單方法

    /**
     * 獲取陣列
     * 之所以沒有加上private訪問修飾符,是因為在CopyOnWriteArraySet類中使用了該方法
     * 加上final防止繼承類去覆寫該方法
     * @return 陣列
     */
    final Object[] getArray() {
        return array;
    }

    /**
     * 設定陣列
     * @param a 新陣列
     */
    final void setArray(Object[] a) {
        array = a;
    }

    /**
     * 獲取陣列元素的個數
     * @return 陣列元素的個數
     */
    public int size() {
        return getArray().length;
    }

    /**
     * 判斷陣列是否為空
     * @return 陣列是否為空
     */
    public boolean isEmpty() {
        return size() == 0;
    }

    /**
     * 判斷兩個物件是否相等
     * @param o1 物件
     * @param o2 物件
     * @return 兩個物件是否相等
     */
    private static boolean eq(Object o1, Object o2) {
        return (o1 == null) ? o2 == null : o1.equals(o2);
    }

    /**
     * 正向遍歷,獲取從指定起始索引到指定結束索引之間搜尋指定元素的索引
     * 若指定區間不存在指定元素的話則返回-1
     * @param o 物件
     * @param elements 陣列
     * @param index 指定起始索引
     * @param fence 指定結束索引
     * @return 指定元素的索引
     */
    private static int indexOf(Object o, Object[] elements,
                               int index, int fence) {
        if (o == null) {
            for (int i = index; i < fence; i++)
                if (elements[i] == null)
                    return i;
        } else {
            for (int i = index; i < fence; i++)
                if (o.equals(elements[i]))
                    return i;
        }
        return -1;
    }

    /**
     * 反向遍歷,獲取從指定起始索引到索引為0之間搜尋指定元素的索引
     * @param o 物件
     * @param elements 陣列
     * @param index 指定起始索引
     * @return 指定元素的索引
     */
    private static int lastIndexOf(Object o, Object[] elements, int index) {
        if (o == null) {
            for (int i = index; i >= 0; i--)
                if (elements[i] == null)
                    return i;
        } else {
            for (int i = index; i >= 0; i--)
                if (o.equals(elements[i]))
                    return i;
        }
        return -1;
    }

    /**
     * 判斷陣列中是否包含指定元素
     * @param o 指定元素
     * @return 陣列是否包含指定元素
     */
    public boolean contains(Object o) {
        Object[] elements = getArray();
        return indexOf(o, elements, 0, elements.length) >= 0;
    }

    /**
     * 正向遍歷,獲取指定元素的索引
     * 若未發現指定元素則返回-1
     * @param o 指定元素
     * @return 指定元素的索引
     */
    public int indexOf(Object o) {
        Object[] elements = getArray();
        return indexOf(o, elements, 0, elements.length);
    }

    /**
     * 正向遍歷,獲取從指定起始索引處開始搜尋指定元素的索引
     * 若未發現指定元素則返回-1
     * @param e 指定元素
     * @param index 指定起始索引
     * @return 指定元素的索引
     */
    public int indexOf(E e, int index) {
        Object[] elements = getArray();
        return indexOf(e, elements, index, elements.length);
    }

    /**
     * 反向遍歷,獲取指定元素的索引
     * @param o 指定元素
     * @return 指定元素的索引
     */
    public int lastIndexOf(Object o) {
        Object[] elements = getArray();
        return lastIndexOf(o, elements, elements.length - 1);
    }

    /**
     * 反向遍歷,獲取從指定起始索引處開始搜尋指定元素的索引
     * @param e 指定元素
     * @param index 指定起始索引
     * @return 指定元素的索引
     */
    public int lastIndexOf(E e, int index) {
        Object[] elements = getArray();
        return lastIndexOf(e, elements, index);
    }

    /**
     * 拷貝新的CopyOnWriteArrayList物件,沒有拷貝物件中的陣列,屬於淺拷貝
     * @return 物件
     */
    public Object clone() {
        try {
            @SuppressWarnings("unchecked")
            CopyOnWriteArrayList<E> clone =
                (CopyOnWriteArrayList<E>) super.clone();
            clone.resetLock();
            return clone;
        } catch (CloneNotSupportedException e) {
            throw new InternalError();
        }
    }

    /**
     * 返回一個包含所有列表元素的有序(按照新增順序)陣列
     * 此方法是建立一個新陣列,方便使用者能夠隨便操作新陣列
     * @return 新陣列
     */
    public Object[] toArray() {
        Object[] elements = getArray();
        return Arrays.copyOf(elements, elements.length);
    }

    /**
     * 將列表的所有元素放入到指定陣列中並返回
     *
     * 注意:T型別要麼是陣列中資料的相同型別,要麼是陣列中資料的父型別,運用多型性質
     * 若傳入的新陣列容量 < 列表容量,則取它的類型別來建立一個包含列表元素的新陣列,並返回
     * 若傳入的新陣列容量 > 列表容量,則將列表中的元素按照順序拷貝到新陣列中,同時將新陣列中索引為size的值設定成null
     * 
     * 一開始我也好奇為啥要在索引為size上設定個null呢?
     * 看了註釋加上自我的理解,若傳入的新陣列是個空陣列的話,那麼除了拷貝列表元素後剩餘的所有空間的值都為null,此時在給索引為size的值設定成null似乎沒有多大
     * 意思;另外一種情況是若傳入的新陣列不是個空陣列,那這個設定就有意義了,傳入的新陣列的某些元素會被列表元素覆蓋,同時有個null,剩下的才是自己本身的資料,呈現這樣子一種效果
     *
     * List<Integer> list = new ArrayList<>();
     * list.add(11);
     *
     * Integer[] str = new Integer[]{1,2,3,4,5,6,7,8,9,10};
     * Integer[] s1 = list.toArray(str);
     *
     * for (Integer s : s1) {
     *     System.out.println(s + ",");
     * }
     *
     * 輸出結果:11,null,3,4,5,6,7,8,9,10,
     * 那麼設定這個null的意義就在於能夠確定列表中元素個數(長度),但有個前提就是呼叫者知道連結串列中的所有節點資訊不存在null才有意義,目前我只有想到這一種情況下有用!
     *
     * @param a 指定陣列
     * @return 填充完列表元素的指定陣列
     */
    public <T> T[] toArray(T a[]) {
        Object[] elements = getArray();
        int len = elements.length;
        if (a.length < len)
            return (T[]) Arrays.copyOf(elements, len, a.getClass());
        else {
            System.arraycopy(elements, 0, a, 0, len);
            if (a.length > len)
                a[len] = null;
            return a;
        }
    }

    /**
     * 判斷陣列中是否包含指定集合中的所有元素
     * 集合中的元素但凡在陣列中未包含則返回false
     * @param c 指定集合
     * @return 陣列中是否包含指定集合中的所有元素
     */
    public boolean containsAll(Collection<?> c) {
        Object[] elements = getArray();
        int len = elements.length;
        for (Object e : c) {
            if (indexOf(e, elements, 0, len) < 0)
                return false;
        }
        return true;
    }

    /**
     * 集合與陣列取交集
     * 最終陣列中只包含與集合共有的元素,相當於在修改陣列
     * @param c 指定集合
     * @return 陣列元素是否被修改成功
     */
    public boolean retainAll(Collection<?> c) {
        if (c == null) throw new NullPointerException();
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (len != 0) {
                // temp array holds those elements we know we want to keep
                int newlen = 0;
                Object[] temp = new Object[len];
                for (int i = 0; i < len; ++i) {
                    Object element = elements[i];
                    if (c.contains(element))
                        temp[newlen++] = element;
                }
                if (newlen != len) {
                    setArray(Arrays.copyOf(temp, newlen));
                    return true;
                }
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 清空陣列中的元素
     */
    public void clear() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            setArray(new Object[0]);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 遍歷陣列,並對陣列中的元素進行指定處理
     * 讀取時不會發生衝突,因為新增、刪除、替換等操作都是使用新副本,只不過會出現實時資料不一致,但最終是一致的
     * @param action 函式式介面,對陣列中的元素指定處理
     */
    public void forEach(Consumer<? super E> action) {
        if (action == null) throw new NullPointerException();
        Object[] elements = getArray();
        int len = elements.length;
        for (int i = 0; i < len; ++i) {
            @SuppressWarnings("unchecked") E e = (E) elements[i];
            action.accept(e);
        }
    }

    /**
     * 根據指定條件移除元素
     * @param filter 使用指定條件來過濾元素
     * @return 是否移除成功
     */
    public boolean removeIf(Predicate<? super E> filter) {
        if (filter == null) throw new NullPointerException();
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (len != 0) {
                int newlen = 0;
                Object[] temp = new Object[len];
                for (int i = 0; i < len; ++i) {
                    @SuppressWarnings("unchecked") E e = (E) elements[i];
                    if (!filter.test(e))
                        temp[newlen++] = e;
                }
                if (newlen != len) {
                    setArray(Arrays.copyOf(temp, newlen));
                    return true;
                }
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 根據指定規則替換所有舊元素
     * operator.apply方法:舊元素作為入參傳入,根據規則返回新元素,然後進行替換
     * @param operator 指定規則,函式式介面
     */
    public void replaceAll(UnaryOperator<E> operator) {
        if (operator == null) throw new NullPointerException();
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len);
            for (int i = 0; i < len; ++i) {
                @SuppressWarnings("unchecked") E e = (E) elements[i];
                newElements[i] = operator.apply(e);
            }
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 根據指定規則對陣列中的元素進行排序
     * 若沒有指定規則則使用預設的升序進行排序
     * 指定規則後會呼叫自定義比較器中的compare方法進行比較排序
     * @param c 自定義比較器,覆寫compare方法
     */
    public void sort(Comparator<? super E> c) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            Object[] newElements = Arrays.copyOf(elements, elements.length);
            @SuppressWarnings("unchecked") E[] es = (E[])newElements;
            Arrays.sort(es, c);
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 自定義序列化
     * 寫入陣列的長度及陣列的元素方便構建
     * @param s 輸出流
     */
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {

        s.defaultWriteObject();

        Object[] elements = getArray();
        // Write out array length
        s.writeInt(elements.length);

        // Write out all elements in the proper order.
        for (Object element : elements)
            s.writeObject(element);
    }

    /**
     * 自定義反序列化
     * @param s 輸入流
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {

        s.defaultReadObject();

        //生成新的鎖物件
        resetLock();

        int len = s.readInt();
        SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, len);
        Object[] elements = new Object[len];

        for (int i = 0; i < len; i++)
            elements[i] = s.readObject();
        setArray(elements);
    }

    /**
     * 獲取陣列元素的字串
     * @return 陣列元素的字串
     */
    public String toString() {
        return Arrays.toString(getArray());
    }

    /**
     * 先判斷當前物件與指定物件是否指向同一個物件,就是在判斷地址
     * 緊接著判斷指定物件屬於List的子類
     * 緊接著獲取該物件的迭代器
     * 若兩個迭代器的元素個數不相等,則返回false
     * 若兩個迭代器的元素個數相等,則將兩個迭代器的元素進行對應的比較,但凡出現對應的元素不相等則返回false
     * @param o 指定物件
     * @return 當前物件與指定物件是否相等
     */
    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof List))
            return false;

        List<?> list = (List<?>)(o);
        Iterator<?> it = list.iterator();
        Object[] elements = getArray();
        int len = elements.length;
        for (int i = 0; i < len; ++i)
            if (!it.hasNext() || !eq(elements[i], it.next()))
                return false;
        if (it.hasNext())
            return false;
        return true;
    }

    /**
     * 獲取雜湊值
     * @return 雜湊值
     */
    public int hashCode() {
        int hashCode = 1;
        Object[] elements = getArray();
        int len = elements.length;
        for (int i = 0; i < len; ++i) {
            Object obj = elements[i];
            hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
        }
        return hashCode;
    }

    /**
     * 獲取分割迭代器
     * 由於該方法涉及到另外一個介面,會另外新起一篇文章來講解該內容,這裡就不做闡述
     * 附上文章地址:http://zlia.tech/2019/08/28/explain-arraylist-spliterator-sourcecode
     * @return 
     */
    public Spliterator<E> spliterator() {
        return Spliterators.spliterator
            (getArray(), Spliterator.IMMUTABLE | Spliterator.ORDERED);
    }


    /**
     * 獲取指定起始索引到指定結束索引之間的元素,簡稱獲取指定子集
     * 指定區間中的元素包括起始索引,不包括結束索引
     * 若起始索引與結束索引相等,則返回空元素
     * 對子集的操作,即呼叫set、add、remove等方法將會影響到整個陣列
     * 但在先獲取子集後,又對整個陣列的結構進行修改,這時在遍歷子集則會導致報錯,而對於整體的非結構性修改則不會報錯,不過依然會影響到子集
     * 所以在獲取子集後最好不要修改陣列的結構
     * 
     * 程式碼片段與ArrayList是類似的,可參考ArrayList文章:http://zlia.tech/2019/08/16/explain-arraylist-sourcecode
     * @param fromIndex 起始索引
     * @param toIndex 結束索引
     * @return 指定區間中的所有元素,稱為子集
     */
    public List<E> subList(int fromIndex, int toIndex) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (fromIndex < 0 || toIndex > len || fromIndex > toIndex)
                throw new IndexOutOfBoundsException();
            return new COWSubList<E>(this, fromIndex, toIndex);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 重置鎖,生成新的鎖物件
     */
    private void resetLock() {
        UNSAFE.putObjectVolatile(this, lockOffset, new ReentrantLock());
    }

獲取元素

    /**
     * 獲取指定索引的元素
     * @param a 陣列
     * @param index 指定索引
     * @return 指定索引的元素
     */
    @SuppressWarnings("unchecked")
    private E get(Object[] a, int index) {
        return (E) a[index];
    }

    /**
     * 獲取指定索引的元素
     * @param index 指定索引
     * @return 指定索引的元素
     */
    public E get(int index) {
        return get(getArray(), index);
    }

修改元素

    /**
     * 將指定索引處的元素修改成指定元素
     * 在執行操作之前,先加上鎖,接著生成陣列的新副本,在新副本中替換元素,最後將陣列指向新副本並釋放鎖
     * 在未釋放鎖之前,其他執行緒無法進入,這樣子就保證了執行緒安全
     * 每次呼叫該方法都會造成新副本陣列的生成,導致記憶體飆升
     * @param index 指定索引
     * @param element 新元素
     * @return 舊元素
     */
    public E set(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            E oldValue = get(elements, index);

            if (oldValue != element) {
                int len = elements.length;
                Object[] newElements = Arrays.copyOf(elements, len);
                newElements[index] = element;
                setArray(newElements);
            } else {
                setArray(elements);
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }

新增元素

    /**
     * 新增指定元素到列表尾部
     * 在執行操作之前,先加上鎖,接著生成陣列的新副本,並擴充其容量,最後將陣列指向新副本並釋放鎖
     * @param e 指定元素
     * @return 是否新增成功
     */
    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 新增指定元素到指定索引處
     * 由於每次都會生成新副本,原先陣列的前index元素列表會拷貝到新副本中,再者原先資料的後index元素列表會拷貝到新副本中
     * 原先陣列中的所有元素都拷貝到了新副本中,最終在新副本中的index位置為null,最後在設定指定元素即可
     * @param index 指定索引
     * @param element 指定元素
     */
    public void add(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (index > len || index < 0)
                throw new IndexOutOfBoundsException("Index: "+index+
                                                    ", Size: "+len);
            Object[] newElements;
            int numMoved = len - index;
            if (numMoved == 0)
                newElements = Arrays.copyOf(elements, len + 1);
            else {
                newElements = new Object[len + 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
            newElements[index] = element;
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 若陣列中未包含指定元素則進行新增到尾部
     * 若陣列中已經存在指定元素則返回false
     * @param e 指定元素
     * @return 是否新增成功
     */
    public boolean addIfAbsent(E e) {
        Object[] snapshot = getArray();
        return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
            addIfAbsent(e, snapshot);
    }

    /**
     * 若新副本中未包含指定元素則進行新增到尾部
     * 若是新副本中已經存在指定元素則返回false
     * 至始至終snapshot都是用來做與新副本進行比較的
     * @param e 指定元素
     * @param snapshot 陣列,有可能成為舊陣列
     * @return 是否新增成功
     */
    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;
            }
            Object[] newElements = Arrays.copyOf(current, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 新增集合中未被陣列包含的元素到陣列尾部,相當於批量新增不存在的元素到尾部
     * 集合中重複的元素只會被新增一次
     * @param c 指定集合
     * @return 新增到陣列中的元素個數
     */
    public int addAllAbsent(Collection<? extends E> c) {
        Object[] cs = c.toArray();
        if (cs.length == 0)
            return 0;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            int added = 0;
            // uniquify and compact elements in cs
            for (int i = 0; i < cs.length; ++i) {
                Object e = cs[i];
                if (indexOf(e, elements, 0, len) < 0 &&
                    indexOf(e, cs, 0, added) < 0)
                    cs[added++] = e;
            }
            if (added > 0) {
                Object[] newElements = Arrays.copyOf(elements, len + added);
                System.arraycopy(cs, 0, newElements, len, added);
                setArray(newElements);
            }
            return added;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 將指定集合新增到陣列尾部
     * @param c 指定集合
     * @return 是否新增成功
     */
    public boolean addAll(Collection<? extends E> c) {
        Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ?
            ((CopyOnWriteArrayList<?>)c).getArray() : c.toArray();
        if (cs.length == 0)
            return false;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (len == 0 && cs.getClass() == Object[].class)
                setArray(cs);
            else {
                Object[] newElements = Arrays.copyOf(elements, len + cs.length);
                System.arraycopy(cs, 0, newElements, len, cs.length);
                setArray(newElements);
            }
            return true;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 將指定集合新增到陣列的指定索引處
     * @param index 指定索引
     * @param c 指定集合
     * @return 是否新增成功
     */
    public boolean addAll(int index, Collection<? extends E> c) {
        Object[] cs = c.toArray();
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (index > len || index < 0)
                throw new IndexOutOfBoundsException("Index: "+index+
                                                    ", Size: "+len);
            if (cs.length == 0)
                return false;
            int numMoved = len - index;
            Object[] newElements;
            if (numMoved == 0)
                newElements = Arrays.copyOf(elements, len + cs.length);
            else {
                newElements = new Object[len + cs.length];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index,
                                 newElements, index + cs.length,
                                 numMoved);
            }
            System.arraycopy(cs, 0, newElements, index, cs.length);
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

移除元素

    /**
     * 移除陣列中指定索引處的元素
     * @param index 指定索引
     * @return 舊元素
     */
    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)
                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);
                setArray(newElements);
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 移除陣列中的指定元素
     * @param o 指定元素
     * @return 是否移除成功
     */
    public boolean remove(Object o) {
        Object[] snapshot = getArray();
        int index = indexOf(o, snapshot, 0, snapshot.length);
        return (index < 0) ? false : remove(o, snapshot, index);
    }

    /**
     * 移除陣列中的指定索引處的元素
     * @param o 指定元素
     * @param snapshot 陣列,有可能是舊陣列
     * @param index 指定索引
     * @return 是否移除成功
     */
    private boolean remove(Object o, Object[] snapshot, int index) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] current = getArray();
            int len = current.length;
            if (snapshot != current) findIndex: {
                //這部分程式碼是獲取新副本中指定元素的索引,也就是獲取最新的索引,看看新副本中有沒有存在等於指定元素的更小索引
                int prefix = Math.min(index, len);
                for (int i = 0; i < prefix; i++) {
                    if (current[i] != snapshot[i] && eq(o, current[i])) {
                        index = i;
                        break findIndex; //表示跳出指定程式碼塊
                    }
                }
                //若在新副本中未找到指定元素的索引則index不會被改變,此時的情況應該是:舊陣列的長度大於新副本陣列的長度,那麼最終會返回false
                if (index >= len)
                    return false;
                //判斷新副本的指定索引處的元素是否與指定元素相等,若相等則說明該位置的元素並未發生改變
                if (current[index] == o)
                    break findIndex;
                //此時的情況是:新副本陣列的長度大於舊陣列的長度,獲取新副本中指定元素的索引
                index = indexOf(o, current, index, len);
                if (index < 0)
                    return false;
            }
            Object[] newElements = new Object[len - 1];
            System.arraycopy(current, 0, newElements, 0, index);
            System.arraycopy(current, index + 1,
                             newElements, index,
                             len - index - 1);
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 移除指定起始索引到結束索引之間的所有元素
     * @param fromIndex 指定起始索引
     * @param toIndex 指定結束索引
     */
    void removeRange(int fromIndex, int toIndex) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;

            if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
                throw new IndexOutOfBoundsException();
            int newlen = len - (toIndex - fromIndex);
            int numMoved = len - toIndex;
            if (numMoved == 0)
                setArray(Arrays.copyOf(elements, newlen));
            else {
                Object[] newElements = new Object[newlen];
                System.arraycopy(elements, 0, newElements, 0, fromIndex);
                System.arraycopy(elements, toIndex, newElements,
                                 fromIndex, numMoved);
                setArray(newElements);
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 陣列中移除指定集合的所有元素
     * @param c 指定集合
     * @return 是否移除成功
     */
    public boolean removeAll(Collection<?> c) {
        if (c == null) throw new NullPointerException();
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (len != 0) {
                // temp array holds those elements we know we want to keep
                int newlen = 0;
                Object[] temp = new Object[len];
                for (int i = 0; i < len; ++i) {
                    Object element = elements[i];
                    if (!c.contains(element))
                        temp[newlen++] = element;
                }
                if (newlen != len) {
                    setArray(Arrays.copyOf(temp, newlen));
                    return true;
                }
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

迭代器

    /**
     * 獲取迭代器
     * @return 迭代器 
     */
    public Iterator<E> iterator() {
        return new COWIterator<E>(getArray(), 0);
    }

    /**
     * 獲取迭代器
     * @return 迭代器
     */
    public ListIterator<E> listIterator() {
        return new COWIterator<E>(getArray(), 0);
    }

    /**
     * 獲取從指定起始索引開始的列表迭代器
     * 列表迭代器中的元素是從指定索引開始到結束索引
     * @param index 指定起始索引
     * @return 列表迭代器
     */
    public ListIterator<E> listIterator(int index) {
        Object[] elements = getArray();
        int len = elements.length;
        if (index < 0 || index > len)
            throw new IndexOutOfBoundsException("Index: "+index);

        return new COWIterator<E>(elements, index);
    }

    /**
     * 列表迭代器,正向迭代
     * 可獲取上一個元素、下一個元素及索引
     */
    static final class COWIterator<E> implements ListIterator<E> {

        //當迭代器被建立後,只拿到當前陣列的引用,也就是說只擁有當前陣列的元素
        //而隨著後面列表的add、remove、repalce都是在操作新副本,這些變動並不會反映到當前的引用,相當於是兩個引用
        private final Object[] snapshot;

        //下一個元素的索引
        private int cursor;

        /**
         * 初始化引數
         * @param elements 當前陣列
         * @param initialCursor 下一個元素的索引
         */
        private COWIterator(Object[] elements, int initialCursor) {
            cursor = initialCursor;
            snapshot = elements;
        }

        /** 
         * 判斷是否有下一個元素
         * @return 是否有下一個元素
         */
        public boolean hasNext() {
            return cursor < snapshot.length;
        }

        /**
         * 判斷是否有前一個元素
         * @return 是否有前一個元素
         */
        public boolean hasPrevious() {
            return cursor > 0;
        }

        /**
         * 獲取下一個元素的值
         * 若不存在則丟擲異常
         * @return 下一個元素的值
         */
        @SuppressWarnings("unchecked")
        public E next() {
            if (! hasNext())
                throw new NoSuchElementException();
            return (E) snapshot[cursor++];
        }
        
        /**
         * 獲取上一個元素
         * 若不存在則丟擲異常
         * @return 上一個元素
         */
        @SuppressWarnings("unchecked")
        public E previous() {
            if (! hasPrevious())
                throw new NoSuchElementException();
            return (E) snapshot[--cursor];
        }

        /**
         * 獲取下一個元素的索引
         * @return 下一個元素的索引
         */
        public int nextIndex() {
            return cursor;
        }

        /**
         * 獲取上一個元素的索引
         * @return 上一個元素的索引
         */
        public int previousIndex() {
            return cursor-1;
        }

        /**
         * 不支援,為什麼不支援在資料結構那一欄中有提到
         */
        public void remove() {
            throw new UnsupportedOperationException();
        }

        /**
         * 不支援,為什麼不支援在資料結構那一欄中有提到
         */
        public void set(E e) {
            throw new UnsupportedOperationException();
        }

        /**
         * 不支援,為什麼不支援在資料結構那一欄中有提到
         */
        public void add(E e) {
            throw new UnsupportedOperationException();
        }

        /**
         * 遍歷元素,只能遍歷一次
         * 與forEach的區別在於:可以遍歷多次
         * @param consumer 函式式介面,宣告如何處理元素的函式
         */
        @Override
        public void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            Object[] elements = snapshot;
            final int size = elements.length;
            for (int i = cursor; i < size; i++) {
                @SuppressWarnings("unchecked") E e = (E) elements[i];
                action.accept(e);
            }
            cursor = size;
        }
    }

總結

  • CopyOnWriteArrayList允許存放Null。

  • 底層通過生成陣列的新副本實現,故記憶體佔用是個明顯的問題。

  • 多執行緒情況下,可能讀取舊資料,只能保證資料的最終一致性。

  • CopyOnWriteArrayList適用於讀多寫少的場景。

  • CopyOnWriteArrayList在效能上沒有ArrayList、LinkedList好,畢竟加了鎖!

  • CopyOnWriteArrayList沒有擴容機制,每次新增節點前就拷貝源陣列到新陣列中,而新陣列與源陣列的長度差為1。

重點關注

執行緒安全 底層是通過生成陣列的新副本實現 由於每次都生成新副本,故記憶體佔用會相對更大 多執行緒情況下,可能讀取到舊資料(讀取在新增之前執行)