探索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。

重点关注

线程安全 底层是通过生成数组的新副本实现 由于每次都生成新副本,故内存占用会相对更大 多线程情况下,可能读取到旧数据(读取在添加之前执行)

posted @ 2020-12-21 22:14  zliawk  阅读(144)  评论(0)    收藏  举报