CopyOnWriteArrayList源码分析

如果你不相信努力和时光,那么成果就会是第一个选择辜负你的。不要去否定你自己的过去,也不要用你的过去牵扯你现在的努力和对未来的展望。不是因为拥有希望你才去努力,而是去努力了,你才有可能看到希望的光芒。CopyOnWriteArrayList源码分析,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

原理分析

继承类图

在这里插入图片描述

成员变量分析

只有两个属性

  • 一个是ReentrantLock类型的变量lock
  • 另一个是一个Object类型的数组变量array
    在这里插入图片描述

主要方法分析

构造函数

  1. setArray(): 这个方法会被后面的构造方法调用,用来设置成员变量array的值
/**
     * Sets the array.
     */
    final void setArray(Object[] a) {
        array = a;
    }
  1. CopyOnWriteArrayList():空参构造方法,可以初始化一个空数
   /**
     * Creates an empty list.
     */
    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }
  1. CopyOnWriteArrayList(Collection<? extends E> c):传入一个集合类,可以把集合类转换成本类中的Object数组
    public CopyOnWriteArrayList(Collection<? extends E> c) {
        Object[] elements;
        //1. 判断传进来的集合是否为CopyOnWriteArrayList类型,如果是,直接赋值给elements
        if (c.getClass() == CopyOnWriteArrayList.class)
            elements = ((CopyOnWriteArrayList<?>)c).getArray();
        else {
            //2.1 先把传进来的集合类转换成数组
            elements = c.toArray();
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elements.getClass() != Object[].class)
            //2.2 把转换来的数组转换成Object类型的数组
                elements = Arrays.copyOf(elements, elements.length, Object[].class);
        }
        setArray(elements);
    }
  1. CopyOnWriteArrayList(E[] toCopyIn):利用传入的数组来初始化array
 public CopyOnWriteArrayList(E[] toCopyIn) {
        setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
    }

增删方法

add方法

  1. add(E e):传入一个元素,把这个元素放在数组的末尾
public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        //1. 加锁(非公平锁)
        lock.lock();
        try {
          //2. 创建一个数组指针,指向当前list的数组对象
            Object[] elements = getArray();
            int len = elements.length;
          //3. 创建一个新的数组对象,长度为当前数组长度+1,并且前面的length个元素都被copy到新数组中
            Object[] newElements = Arrays.copyOf(elements, len + 1);
          //4.让数组末尾的元素为新加入的元素
            newElements[len] = e;
          //5.设置新数组为array
            setArray(newElements);
            return true;
        } finally {
          //6. 解锁
            lock.unlock();
        }
   }
  1. add(int index, E element):在当前list的指定位置插入指定元素
public void add(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //1. 创建一个指向当前array的对象指针
            Object[] elements = getArray();
            int len = elements.length;
            if (index > len || index < 0)
                throw new IndexOutOfBoundsException("Index: "+index+
                                                   ", Size: "+len);
            //2. 定义新数组对象
            Object[] newElements;
            //3. 得到需要移动的元素数目
            int numMoved = len - index;
            //4. 如果要移动的元素是0的话直接当前数组长度加1就行
            if (numMoved == 0)
                newElements = Arrays.copyOf(elements, len + 1);
            else {
            //5.如果要移动的元素非0的话要向右移动numMoved个元素
                newElements = new Object[len + 1];
                //5.1 先复制下标为 0-index 的元素在新数组中
                System.arraycopy(elements, 0, newElements, 0, index);
                //5.2 再复制下标为 index+1 - len的元素在新数组中
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
            newElements[index] = element;
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

get方法

public E get(int index) {
        return get(getArray(), index);
    }

remove方法

public E remove(int index) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
        //1. 创建一个指向当前array的对象指针
            Object[] elements = getArray();
            int len = elements.length;
            E oldValue = get(elements, index);
        //2. 计算需要移动的元素数目
            int numMoved = len - index - 1;
            if (numMoved == 0)
                setArray(Arrays.copyOf(elements, len - 1));
            else {
                Object[] newElements = new Object[len - 1];
                //复制elements从0开始,长度为index的元素
                System.arraycopy(elements, 0, newElements, 0, index);
                //复制elements从index+1开始,长度为numMoved的元素
                System.arraycopy(elements, index + 1, newElements, index,
                                 numMoved);
                setArray(newElements);
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }

扩展方法

java.lang.System#arraycopy:

public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
 
                                     int length);
src:源数组;
srcPos:源数组要复制的起始位置;
dest:目的数组;
destPos:目的数组放置的起始位置;
length:复制的长度。
注意:src and dest都必须是同类型或者可以进行转换类型的数组.

小结

  • CopyOnWriteArrayList顾名思义,在写入操作时,copy源数组到新的数组中,而读取时,是从源数组去读的,因为写入操作是在另外一个数组中执行,因此在读取时,不用进行线程同步,但是要注意一点,copy数组的开销在数据量大的情况下,非常耗资源,因此,它的使用场景,适合于读取远大于写入操作的场景。当然,在写入时,是有锁的,JDK中的实现是采用重入显式锁进行锁定的。当写操作完成以后再将源数组的引用指向copy的数组,最后释放锁

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/202531.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!